zoukankan      html  css  js  c++  java
  • Linux 网络编程——TCP

    环境:Linux  C
     
    一、协议介绍
         TCP是面向连接的协议,提供可靠的数据传输;TCP协议的可靠传输基于三次握手、四次挥手以及确认重传机制实现。下面来具体展示下TCP的三次握手、四次挥手状态
        大家都知道已经建立连接的TCP遇到网络丢包会有确认重传机制。在三次握手期间,如果A收到B的SYN+ACK,但是B没有收到A返回的ACK,此时B超时后会重传SYN+ACK,如果超过特定次数依然没有收到A的ACK,那么B向A发送RST包,关闭连接,避免A维护一个异常的连接。四次挥手也是类似,都是利用超时重传机制。
     
    二、TCP网络编程
         先展示一段代码
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <errno.h>
     
    int main(int argc, char *argv[])
    {
        int fd;
        struct sockaddr_in addr;
        struct timeval timeo = {3, 0};
        //lingger参数1表示启用linger,5最多等待5秒或者socket发送队列中的消息发送完毕
        //再执行close操作
        struct linger linger_value = {1, 5};
        socklen_t len = sizeof(timeo);
     
        //创建TCP连接,SOCK_CLOEXEC指明在执行fork等创建子进程函数时,子进程将继承的该fd描述符默认关闭
        fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
        if (argc == 4)
            timeo.tv_sec = atoi(argv[3]);
     
        //设置接受socket数据的等待时间,对read,recv,recvfrom等函数生效
        setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len);
     
        //设置该参数是为了保证在关闭前尽可能的将数据都发送出去
        setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_value, (socklen_t)sizeof(linger_value));
     
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(argv[1]);
        addr.sin_port = htons(atoi(argv[2]));
     
        //connect受SO_SNDTIMEO设置影响,最多等待timeo时间
        if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
            if (errno == EINPROGRESS) {
                fprintf(stderr, "timeout
    ");
                return -1;
            }
            perror("connect");
            return -1;
        }
        printf("connected
    ");
     
        char *msg = "hello buddy !";
        //这里指定MSG_NOSIGNAL表示当fd已经关闭时,不会触发SIGPIPE信号(该信号的默认操作是关闭程序
        send(fd, msg, strlen(msg), MSG_NOSIGNAL);
     
        char buffer[1024] = {''};
        //这里的MSG_DONTWAIT就是要求recv函数非阻塞执行 ,同给socket设置SO_NONBLOCK效果一样
        recv(fd, buffer, sizeof(buffer) - 1, MSG_NOSIGNAL | MSG_DONTWAIT);
     
        //向对端发送FIN报文,关闭该fd上所有的读写socket连接
        shutdown(SHUT_RDWR, fd);
     
        //关闭文件描述符,如果执行了fork函数且没有设置SOCK_CLOEXEC,需要在子进程中也执行close关闭继承的文件描述符
        close(fd);
     
        return 0;
    }
    

    注:该代码是客户端的一部分代码,服务端与这个类似,区别就是服务端用bind、listen、accept函数替代了connect函数来实现对服务端口的监听、新连接的处理。

    1. 编写多进程程序时,如果创建文件描述符(打开文件、创建socket等)都需要设置SOCK_CLOEXEC标识,避免出现副作用。
    2. 给socket设置SO_LINGER选项、调用close之前执行shutdown命令一般用于服务端,这样可以最大限度地保证将数据发送到客户端并且可以有效防止,因为fork导致的子进程在处理socket时调用close关闭fd描述符,由于fd原有计数是2,所以fd并未实际关闭,也就不会触发向客户端发送FIN报文结束连接的动作。因而,服务端就可能出现大量的CLOSE_WAIT的连接。
    3. 如果系统收到FIN报文,会让recv、read等函数返回0表示连接已经关闭。此时send、recv等读写函数再对目标fd进行读写,根据TCP协议规定,会收到一个RST报文,如果再读写的话系统就会发送SIGPIPE信号给调用进程,而该信号默认动作是结束进程。因此,要么调用信号处理函数将SIGPIPE捕获处理,要么像代码中那样设置MSG_NOSIGNAL选项避免产生SIGPIPE信号。
    4. SO_SNDTIMEO除了对send、write等有效外,还对connect有效;SO_RCVTIMEO除了对recv、read等有效外还对accept有效。
    5. 如果系统中出现大量的close_wait状态的TCP连接,说明是程序在收到对端的FIN报文后没有关闭相关的socket(有可能是fork这类函数造成的),这需要检查结束连接处理部分的代码。
    6. 如果系统中出现大量的time_wait状态的TCP连接,这种连接是正常的。出现这种连接都是由于主动关闭socket造成的,解决方案就是改成客户端主动结束连接、或者降低time_wait的时间。
  • 相关阅读:
    TCP 协议如何保证可靠传输
    mysql 优化
    Navicat 导入导出
    Hibernate的优缺点
    寒假学习日报(十八)
    《机器学习十讲》第二讲总结
    寒假学习日报(十七)
    《设计原本》阅读笔记(二)
    《机器学习十讲》第一讲总结
    寒假学习日报(十六)
  • 原文地址:https://www.cnblogs.com/sxhlinux/p/6765865.html
Copyright © 2011-2022 走看看