一、结构划分
- Tcp套接字编程:第二、四、五章、
- Udp套接字编程:第二、八、二十二章
- I/O:第六章 I/O复用、第十四章 高级I/O函数、第十六章非阻塞式I/O
- 范式: 第三十章
二、第二章
2.1 TCP
- 连接
- 可靠性。需要有ack包确认,否则自动重传,直至数次的重传失败才放弃
- 基于seq对数据进行排序去重,然后才返回给应用
- 流量控制。根据自身的接受缓冲区大小,告知对方能接收多少字节的数据,从而避免接收缓冲区溢出
- 全双工。 双方都能发送数据, 和接收数据
- TIME_WAIT状态。执行主动关闭的一方;若主动关闭的一方,最后的ACK包发送失败,被动关闭的一方将重新发送FIN, 此时主动关闭一方需要能够正确重传最终的ACK包;新的连接不能复用还在TIME_WAIT状态的连接
2.2 tcp与udp输出
- tcp需要收到对端的ack包后,才能把发送缓存区中的已确认的包删除
- udp则没有一个真正的缓存区, 数据包发送出去后就删除
三、第四、五章 TCP编程
- listen函数。两个队列,一个是未完成连接(三次握手)队列, 一个是已完成连接队列。 accept从已完成连接队列返回。 当客户端的SYN到达发现未完成连接队列是满的,则忽略,等客户端重新请求,因为这是考虑预期队列满的情况是暂时的
- 并发服务器的基本框架
pid_t pid; int listenfd, connfd; listenfd = Socket(...); Bind(listenfd, ...); Listen(listenfd, ...); for( ; ; ) { connfd = Accept(listenfd, ...); if ( (pid==Fork()) == 0) { // child process Close(listenfd); deal_msg(connfd); Close(connfd); exit(0); } Close(connfd); // parent process }
四、第八、二十二章 udp编程
- udp的connect函数。 没有三次握手,只是保存对端的ip、端口; 提高效率,发多个数据包时,少了多次建立/断开连接的过程; 用write/send代替sendto, 用read/recv代替 recvfrom
- udp服务器的基本框架
sockfd = Socket(.., SOCK_DGRAM ,..); Bind(sockfd,..); while(1){ n = Recvfrom(sockfd, ..); doSomething(); Sendto(sockfd, ..); }
- udp传输的可靠性。 超时重传(重传定时器是 基于rtt动态变化的,超时重传算法;rtt的计算可以通过透传客户端的时间戳,这样无需同步服务器和客户端的时钟); 序列号(验证请求和响应的匹配)
- 并发udp。若是单次交互则无所谓。 若是需要交换多个数据包, fork一个子进程绑定一个不同的端口, 并在第一个回包中告知客户端,后续的请求包发到新的端口
五、第六、十四、十六章 I/O
- Select 代码框架
// 1. 初始化 fd_set allset; FD_ZERO(&allset); FD_SET(listenfd, &allset) // 加入select的遍历集合 // 2. 循环接受新的conn, 以及检查现有的conn是否可读了 while(1) { Select(maxfdp1, &allset, NULL, NULL, NULL); // 检车connFdQue中有没有ready的 for connFdQue { if IS_SET(connfd, &allset) { //recv msg and doSomething; } } if(IS_SET(listenfd,&allset)) { connfd = accept(listenfd, ..); connFdQue.add(connfd); // 新连的connfd加入select遍历集合 FD_SET(connfd, &allset) } }
- Poll代码框架
client[0].fd = listenfd; client[0].events = POLLRDNORM // 普通数据读写 while(1) { nready = Poll(client, maxfd+1, 3000) If(client[0].revents) { //新的连接到达 Connfd = accept(listenfd, …) Client[i].fd = connfd Client[i].events = POLLRDNORM } For client { //已连接的fd有ready If (client[i].revents){ //recvmsg and do something } } }
- 非阻塞套接字。 读写, epollo的边缘模式下, 服务器反复读直到返回EAGAIN/EWOULDBLOCK, 才结束; accept: 若accept返回的是EWOULDBLOCK错误, 则忽略继续,防止出现客户端终止连接,然后又没有新完成的连接,这样服务器就一直阻塞在accept操作