1 网络IO编程要点
1.1 连接建立
服务器接收客户端连接:
- 注意三次握手时机,服务器在接收客户端返回ack才是加入到全连接队列;
- accpet函数将全连接队列数据拷贝到用户区。
//backlog为全连接队列的大小
listen(sockfd, backlog);
int clientfd = accept(listenfd, &addr, &size);
客户端连接服务器:
- 客户端连接服务器成功需要客户端发送ack成功才能成功
int connectfd = socket(AF_INET, SOCK_STREAM, 0);
connect(connectfd, (struct sockaddr *)&addr, sizeof(addr));
1.2 连接断开
主动断开:
- 主动断开分为三种模式,关闭读写端,关闭读端,关闭写端
//关闭读写端
close(fd);
shutdown(SHUT_RDWR);
//关闭读端
shutdown(SHUT_RD);
//关闭写端
shutdown(SHUT_WR);
被动断开:
- 对于需要进行半连接操作的,可以利用读端或者写端关闭;操作对于的写端和读端
- read返回值为0 为读端被动关闭;
- write返回值为-1 同时errno数值为EPIPE为写端被动关闭;
//接收端读端被动关闭;发送端写端关闭。
int n = read(fd, buf, size);
if (n == 0) {
read_close(fd);//可以进行写相关操作,之后close(fd);
}
//被动写端关闭
int n = write(fd, buf, size);
if (n == -1 && errno == EPIPE) {
write_close(fd);//可以进行读相关操作,之后close(fd);
}
1.3 数据到达
- 消息到达需要忽略EINTR系统中断错误;
- 消息到达需要忽略EWOULDBLOCK接收缓冲区为空的错误,当没有数据需要退出循环读数据;
- 其他错误需要:调用close关闭连接
while(1)
{
int n = read(fd, buf, size);
if (n < 0) { // n == -1
if (errno == EINTR || errno == EWOULDBLOCK)
break;
close(fd);
} else if (n == 0) {
close(fd);
} else {
// 处理接收数据
}
}
1.4 数据发送完毕
- EINTR系统中断直接返回;等待下一次触发写;
- EWOULDBLOCK写缓冲区满返回;等待下一次写;
- 其他错误关闭连接;
- 注意写的大小一定是等于size的大小,当不等于的情况一般是返回失败的。
int n = write(fd, buf, size);
if (n == -1) {
if (errno == EINTR || errno == EWOULDBLOCK) {
return;
}
close(fd);
}
1.5 非阻塞IO
- 阻塞非阻塞其实是阻塞在网络线程
- 只需要对socketfd设置非阻塞属性既可以保证IO函数是非阻塞的
- 阻塞与非阻塞差异:IO函数在数据未到达是否立即返回
// 默认情况下,fd 是阻塞的,设置非阻塞利用fcntl;
int flag = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flag | O_NONBLOCK);