一:非阻塞socket
1:阻塞socket和非阻塞socket区别读操作
对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返回。当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数。当sockt的接收缓冲区中的数据大于期望读取的字节数时,读取期望读取的字节数,返回实际读取的长度。
对于非阻塞socket,socket的接收缓冲区中有没有数据,read调用都会立刻返回,返回错误号为EWOULDBLOCK。接收缓冲区中有数据时,与阻塞socket有数据的情况是一样的
//非阻塞读取
if ((nread = read(sock_fd, buffer, len)) < 0)
{
if (errno == EWOULDBLOCK)
{
return 0; //表示没有读到数据
}
else return -1; //表示读取失败
}
else return nread;读到数据长度
写操作
非阻塞socket在发送缓冲区没有空间时会直接返回错误号EWOULDBLOCK,如果发送缓冲区中有足够空间或者是不足以拷贝所有待发送数据的空间的话,则拷贝前面N个能够容纳的数据,返回实际拷贝的字节数。
对于阻塞Socket而言,如果发送缓冲区没有空间或者空间不足的话,write操作会直接阻塞住,直到有足够空间拷贝所有数据到发送缓冲区,然后返回。
//非阻塞写
nt write_pos = 0;
int nLeft = nLen;
while (nLeft > 0)
{
int nWrite = 0;
if ((nWrite = write(sock_fd, data + write_pos, nLeft)) <= 0)
{
if (errno == EWOULDBLOCK)
{
nWrite = 0;
}
else return -1; //表示写失败
}
nLeft -= nWrite;
write_pos += nWrite;
}
return nLen;
三:非阻塞建立连接
阻塞方式下,connect首先发送SYN请求道服务器,当客户端收到服务器返回的SYN的确认时,则connect,否则的话一直阻塞.非阻塞方式,connect将启用TCP协议的三次握手,但是connect函数并不等待连接建立好才返回,而是立即返回。返回的错误码为EINPROGRESS,表示正在进行某种过程.
四:非阻塞接收连接
阻塞方式的监听socket,accept在连接队列中没有建立好的连接时将阻塞,直到有可用的连接,才返回。非阻塞倾听socket,在有没有连接时都立即返回,返回的错误码为EWOULDBLOCK
五:无阻塞的设置方法
方法一:fcntlint flag;
if (flag = fcntl(fd, F_GETFL, 0) <0) perror("get flag");
flag |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flag) < 0)
perror("set flag");
方法二:ioctl
int b_on = 1;
ioctl (fd, FIONBIO, &b_on);
六:设置端口复用
服务端程序有时重启失败会报告“bind:address in use”,这是因为TCP断开连接里有个TIME_WAIT状态(一版持续2*MSL)导致端口没被完全释放,不能被重新绑定。如果在bind端口之前设置SO_REUSEADDR,就可避免这种状况
SO_REUSEADDR表示可以重用本地地址和端口,但由于存在TIME_WAIT状态,服务端程序有可能会收到非期望数据
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags)) == -1){
printf("setsockopt failed SO_REUSEADDR\n");
}
七:设置收发包大小
系统默认发送和接收一次为8688字节linux下增加发送和接收缓存区大小,还要配合设置系统文件:
cat /proc/sys/net/ipv4/tcp_rmem(默认接收缓存区大小)
cat /proc/sys/net/ipv4/tcp_wmem(默认发送缓存区大小)
cat /proc/sys/net/core/rmem_max(最大接收缓存区大小)
cat /proc/sys/net/core/wmem_max(最大发送缓存区大小)
rmem_max是最大接收缓存区大小设置值iRcvBufLen一半
if(setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &iRcvBufLen, sizeof(iRcvBufLen)) == -1)
{
printf("setsockopt SO_RCVBUF failed\n");
}
if(setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &iSndBufLen, sizeof(iSndBufLen)) == -1)
{
LOG("setsockopt SO_RCVBUF failed (%s)!", ERRSTR);
}
八:多路复用
多路复用能有效避免进程为等待一个已经连接上的客户端的数据而无法处理其它连接,有效提高了服务端的并发数,多路复用最常见的是epoll和selectlinux提供select/eopll函数来实现多路复用输入/输出模型。select/epoll系统调用是用来让程序监视多个文件句柄变化的。程序会一直等待,直至被监视的文件句柄有一个或多个发生了状态改变。