P13 select 模型上
电脑承受进程数量 1核2GB 可以大约 100 多进程或线程 普通服务器大概是 10倍性能,即 1000 多进程或线程。 IO 多路复用能力 1个进程或线程可以使用多个 TCP。 单进程 select 可以 1024 单进程 poll 可以数千 单进程 epoll 可以百万 fdset 本质上是 int[32] 组成的 bitmap, 刚好对应 1024 个文件描述符。 参考 IO 多路复用 select 模型上
电脑承受进程数量 1核2GB 可以大约 100 多进程或线程 普通服务器大概是 10倍性能,即 1000 多进程或线程。 IO 多路复用能力 1个进程或线程可以使用多个 TCP。 单进程 select 可以 1024 单进程 poll 可以数千 单进程 epoll 可以百万 fdset 本质上是 int[32] 组成的 bitmap, 刚好对应 1024 个文件描述符。 参考 IO 多路复用 select 模型上
客户端和服务端字符串转字节为啥不一样? 客户端 struct hostent* h; // 用于存放服务端IP地址(大端序)的结构体的指针。 if ( (h = gethostbyname(argv[1])) == nullptr ) // 把域名/主机名/字符串格式的IP转换成结构体。 { cout << "gethostbyname failed.\n" << endl; close(sockfd); return -1; } memcpy(&servaddr.sin_addr,h->h_addr,h->h_length); // ③指定服务端的IP(大端序)。 服务端 servaddr.sin_addr.s_addr=inet_addr(argv[1]); // ③指定服务端的IP,只能用IP,不能用域名和主机名。 原因 inet_addr 这个只能转换 ip 地址 gethostbyname 可以支持 IP,域名等等。 对于服务端来说,给他 ip 地址就够了。但是对于客户端来说,他需要连接的更多的可能是域名,而不是 IP。 服务端为什么也要设置 IP 如果服务端是运行于多网卡的服务器上面,那么必须要明确当前这个服务端是服务于那一个网段的,通过设置 servaddr.sin_addr.s_addr 就可以确定服务的网段了。 参考 万恶的结构体
getsockopt 作用 可以获取 socket 缓冲区大小。 int bufsize = 0; socklen_t optlen = sizeof(bufsize); getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &bufsize, &optlen); // 获取发送缓冲区的大小。 cout << "send bufsize=" << bufsize << endl; getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &bufsize, &optlen); // 获取接收缓冲区的大小。 cout << "recv bufsize=" << bufsize << endl; send 会阻塞吗? 如果发送太快,接受太慢,send 也会被阻塞。 send 之后,立即关闭,能接收到吗? 可以的,因为 send 是把数据写入到内核中的发送缓冲区,就算立即关闭,也能接收到。 nagle 算法 在发送一个小于 MSS 长度的包之后,必须收到回复,才能发下一个小包。保证了网络中小包数量得到控制。一般 ACK 大概 40ms. 接收延迟:在接受到一个包之后,会等待 40ms,才会发送 ACK, 这样可以尽量和应用层的回复数据合一个包。 在联机游戏,证券交易等应用场景,时效性要求高,一般会禁用 Nagle 算法。开启 TCP_NODELAY 选项。 #include <netinet/tcp....
netstat 使用 netstat 可以用来查看 socket 状态。 安装 如果系统里面没有,那么 yum install net-tools -y 可以安装。 使用 使用 netstat -a | less 或者 netstat -a | more 来查看。 上半部分的信息是 TCP 相关,下半部分是进程间的 socket 通信。 可以观察右边的 listen, established 等状态。 客户端的 port 是客户端系统随机选择的,不要去太过于关注。 port 使用限制 普通权限只能使用 1024 以上端口 root 可以使用 1024 以下的端口。 close 状态是假想状态,不存在的。 listen 的第二个参数作用 第二个参数 + 1 是 established 队列的大小,超过队列的客户端连接状态为 SYN_RCV. 三次握手 客户端申请向服务端的通道 服务端接受了,并且向客户端申请通道 客户端接受了,并给出回应 这样经过三次握手,双方建立了双向通道 四次挥手 A 请求关闭 B 同意关闭单向通道 B 把剩余的数据全部发送。 B 向 A 发送关闭请求 A 发送回应 主动端开的 A 在最后的回应后有个 TIME_WAIT 状态,时间为 2MSL,一个 MSL 在 30秒到1分钟。 服务端主动关闭 会导致 socket 释放,并且 2MSL 后端口才能重用。否则会有 bind 错误提示,并且在 netstat 中可以看到处于 TIME_WAIT 状态。 可以使用 setsockopt 函数,在 bind 之前,可以防止 bind 错误。 客户端主动关闭 没有危害,因为客户端的端口是随机分配端口号的,并且一般用的不多,够用了。...
signal(i, SIG_IGN) 作用 // 忽略全部的信号,不希望被打扰。顺便解决了僵尸进程的问题。 for (int ii=1;ii<=64;ii++) signal(ii,SIG_IGN); kill(0, SIGTERM) 作用 kill(0,SIGTERM); // 向全部的子进程发送15的信号,通知它们退出。 因为在子进程中 SIGTERM 是退出信号。 父进程关掉 clientsocket, 子进程关掉 listensocket int pid=fork(); if (pid==-1) { perror("fork()"); return -1; } // 系统资源不足。 if (pid> 0) { // 父进程。 tcpserver.closeclient(); // 父进程关闭客户端连接的socket。 continue; // 父进程返回到循环开始的位置,继续受理客户端的连接。 } tcpserver.closelisten(); // 子进程关闭监听的socket。 在 /proc/xxx 下面的是进程相关资源,里面的 fd 是根据当前最小可用来递增的。 如果父进程不关 clientsocket, 那么每次 accept 之后,fd 都需要增加一个,然后在后面的 fork 中,这些递增的 fd 也都会被复制到子进程中。对于子进程来说,它不需要其他子进程相关的 =clientsocket=,所以完全是浪费资源。 子进程只需要自己对应的 clientsocket, 不需要 listensocket. listensocket 是专门给父进程用来监听的。 子进程结束需要 return return 0; // 子进程一定要退出,否则又会回到accept()函数的位置。 如果子进程结束没有 return=,让进程退出,那么会在 =while 中重新回到 accept 这个地方,进行了监听,然后 fork 了。...
为什么 TCP 是可靠的通信,服务端每一步都要有回复。 因为服务端可能没有 accept 或者可能忙于其他事情,bug 掉线等等。所以虽然 TCP 本身是可靠的,但是服务器可能是不可靠的。所以为了业务是正常的,就必须在服务端正常应答的情况下,才能继续。 读取文件使用 ifstream 操作 字符串拼接报错? 一般情况都是只有 char * 和 char * 拼接,才会报错。只要语句中有一个 string, 拼接就不会报错。 参考 实现文件传输功能
封装时 send 函数为啥使用 const string & buf 作为参数类型 bool send(const string &buffer) // buffer不要用const char * const string & buf 既可以接收 string 也可以接受 char *. 估计是对 char * 进行了隐式转换成了 string. send 函数的参数为啥是 buf.data() 和 buf.size(), 而不是 buf.c_str() 和 buf.length if ((::send(m_clientfd,buffer.data(),buffer.size(),0))<=0) return false; buf.data() 和 buf.c_str() 都指向的是内容的指针,但是语义不一样。 data 表示的就是原始数据,而 c_str 表示的是转化为 char *. buf.size() 的语义是原始内容的大小,而 length 表示的是字符串的长度。 所以都是因为语义不合适。虽然都能用,但是确实不合适。 recv 函数的参数为啥是 & buf[0] 而不是 buf.c_str() 或者 buf.data() int readn=::recv(m_clientfd,&buffer[0],buffer.size(),0); // 直接操作buffer的内存。 因为后面两种返回的都是带有 const, 只有 &buf[0] 才没有 const, 适合对内容进行修改。...
需求 编程调试的时候,如果用到了消息队列,难免会消息队列中有残余信息的情况,并且如果希望在服务程序结束前就清除这些消息队列,那就需要在 shell 中使用命令进行删除。 解决 消息队列属于 IPC 的一部分,使用的命令也是 IPC 的系列命令中的 ipcrm. 查看消息队列情况 ipcs -q 删除指定 key 的消息队列,一般我们经常用这个,因为 key 一般是我们自己设定的。 ipcrm -Q 1234 删除指定 id 的消息队列 ipcrm -q 1234 如果程序是使用 sudo 来启动的,那么删除的时候,也需要 sudo, 否则没有权限. 参考 ipcs, ipcrm 命令 linux下消息队列的查看与删除(ipcs&ipcrm的使用) 请问Linux下如何清除消息队列queue中的内容??
需求 因为需要在多个进程之间通过消息队列传输数据,所以需要加大消息队列的大小。 解决 首先确定使用的是 System V 还是 POSIX 的消息队列,这两种从 API 到配置都不一样。本文主要提的是 System V 的消息队列。 System V 查看当前系统中的设置 cat /proc/sys/kernel/msgmni cat /proc/sys/kernel/msgmax cat /proc/sys/kernel/msgmnb # 查看当前使用 ipcs -u # 查看限制 ipcs -l 修改配置 System V 的消息队列配置文件是 /etc/sysctl.conf 这个总的配置文件或者 /etc/sysctl.d/ 文件夹下面的细分配置文件。 sudo vim /etc/sysctl.d/11-message-queue.conf # set message queue # set message number of any queue kernel.msgmni = 312 # set one message max size kernel.msgmax = 8192 # set all message totall max size kernel....
需求 在使用串口调试助手调试读写程序时,发现在开启定时 10ms 发送的情况下,接收到的数据有时候会不对。 解决 原因 经过反复调试,最终确定问题是 RS485 导致的问题。因为下面几个原因叠加导致: RS485 是半双工设备,同时只能收或者发。 串口调试助手,定时发送没有办法刚好完全避开程序的发送 所以,就出现了,当程序在发送时,串口调试助手也在发送,等到程序接收的时候,数据就不完整,或者有问题。 验证 验证也很简单: 程序只收不发,然后查看所有受到的数据,没有错误。 程序发送的间隔拉的很长,观察在发送间隔中的接收数据,也是没有错误的。 参考