UNP重读

Posted by jabin on November 20, 2020

一、结构划分

  1. Tcp套接字编程:第二、四、五章、
  2. Udp套接字编程:第二、八、二十二章
  3. I/O:第六章 I/O复用、第十四章 高级I/O函数、第十六章非阻塞式I/O
  4. 范式: 第三十章

二、第二章

2.1 TCP

  1. 连接
  2. 可靠性。需要有ack包确认,否则自动重传,直至数次的重传失败才放弃
  3. 基于seq对数据进行排序去重,然后才返回给应用
  4. 流量控制。根据自身的接受缓冲区大小,告知对方能接收多少字节的数据,从而避免接收缓冲区溢出
  5. 全双工。 双方都能发送数据, 和接收数据
  6. TIME_WAIT状态。执行主动关闭的一方;若主动关闭的一方,最后的ACK包发送失败,被动关闭的一方将重新发送FIN, 此时主动关闭一方需要能够正确重传最终的ACK包;新的连接不能复用还在TIME_WAIT状态的连接

2.2 tcp与udp输出

  1. tcp需要收到对端的ack包后,才能把发送缓存区中的已确认的包删除
  2. udp则没有一个真正的缓存区, 数据包发送出去后就删除

三、第四、五章 TCP编程

  1. listen函数。两个队列,一个是未完成连接(三次握手)队列, 一个是已完成连接队列。 accept从已完成连接队列返回。 当客户端的SYN到达发现未完成连接队列是满的,则忽略,等客户端重新请求,因为这是考虑预期队列满的情况是暂时的
  2. 并发服务器的基本框架
    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编程

  1. udp的connect函数。 没有三次握手,只是保存对端的ip、端口; 提高效率,发多个数据包时,少了多次建立/断开连接的过程; 用write/send代替sendto, 用read/recv代替 recvfrom
  2. udp服务器的基本框架
    sockfd = Socket(.., SOCK_DGRAM ,..);
    Bind(sockfd,..);
    while(1){
     n = Recvfrom(sockfd, ..);
     doSomething();
     Sendto(sockfd, ..);	
    }
    
  3. udp传输的可靠性。 超时重传(重传定时器是 基于rtt动态变化的,超时重传算法;rtt的计算可以通过透传客户端的时间戳,这样无需同步服务器和客户端的时钟); 序列号(验证请求和响应的匹配)
  4. 并发udp。若是单次交互则无所谓。 若是需要交换多个数据包, fork一个子进程绑定一个不同的端口, 并在第一个回包中告知客户端,后续的请求包发到新的端口

五、第六、十四、十六章 I/O

  1. 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)
     }
    }
    
  2. 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
         }
     }
    }
    
  3. 非阻塞套接字。 读写, epollo的边缘模式下, 服务器反复读直到返回EAGAIN/EWOULDBLOCK, 才结束; accept: 若accept返回的是EWOULDBLOCK错误, 则忽略继续,防止出现客户端终止连接,然后又没有新完成的连接,这样服务器就一直阻塞在accept操作