TCP

三次握手,四次挥手

TCP server

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    int skfd, cnfd, addr_len;
    struct sockaddr_in srv_addr, clt_addr;
    int portnumber=8888;
    char hello[] = "Hello! Long time no see.\n";
    if (2 != argc || 0 > (portnumber = atoi(argv[1])))
    {
        printf("Usage:%s prot\n", argv[0]);
        exit(1);
    } 
    /* 创建IPv4的流式套接字描述符 */
    if (-1 == (skfd = socket(AF_INET, SOCK_STREAM, 0)))
    {
        perror("Socket Error:");
        exit(1);
    }
    /* 填充服务器端sockaddr地址结构 */
    bzero(&srv_addr, sizeof(struct sockaddr_in));
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    srv_addr.sin_port = htons(portnumber);
    /* 将套接字描述符skfd和地址信息结构体绑定起来 */
    if (-1 == bind(skfd, (struct sockaddr *)(&srv_addr), sizeof(struct sockaddr)))
    {
        perror("Bind error");
        exit(1);
    }
    /* 将skfd转换为被动监听模式 */
    if (-1 == listen(skfd, 4))
    {
        perror("Listen error:");
        exit(1);
    }
    while (1)
    {
        /* 调用accept,服务器端一直阻塞,直到客户程序与其建立连接成功为止 */
        addr_len = sizeof(struct sockaddr_in);
        if (-1 == (cnfd = accept(skfd, (struct sockaddr *)(&clt_addr), &addr_len)))
        {
            perror("Accept error:");
            exit(1);
        }
        printf("Connect from %s:%u ...!\n", inet_ntoa(clt_addr.sin_addr), ntohs(clt_addr.sin_port));
        if (-1 == write(cnfd, hello, strlen(hello)))
        {
            perror("Send error");
            exit(1);
        }
        close(cnfd);
    }
    close(skfd);
    exit(0);
}

GDB调试

抓包分析

  • 开启远程抓包,连接tcpServer
./rpcapd -n -d -p10033

nc 192.168.146.129 8888
  • 三次握手建立连接
  • 传输数据,回复ACK,序号
  • 四次挥手断开连接

TCP状态

三次握手状态

四次挥手状态

11种状态说明

半关闭

主动方发生在FIN_WAIT_2状态,这个状态时,主动方不可以在应用层发送数据了,但是应用层还可以接收数据,这个状态称为半关闭

#include <sys/socket.h>
int shutdown(int sockfd, int how);
sockfd: 需要关闭的socket的描述符
how:    允许为shutdown操作选择以下几种方式:
    SHUT_RD(0):    关闭sockfd上的读功能,此选项将不允许sockfd进行读操作。
                   该套接字不再接收数据,任何当前在套接字接受缓冲区的数据将被无声的丢弃掉。
    SHUT_WR(1):     关闭sockfd的写功能,此选项将不允许sockfd进行写操作。进程不能在对此套接字发出写操作。
    SHUT_RDWR(2):   关闭sockfd的读写功能。相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR

2MSL

2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,当TCP的一端发起主动关闭,在发出最后一个ACK包后,
即第3次握 手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间,
等待2MSL时间主要目的是怕最后一个 ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。
在TIME_WAIT状态 时两端的端口不能使用,要等到2MSL时间结束才可继续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。
不过在实际应用中可以通过设置 SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口
ime_wait状态到底持续多长时间?
MSL 为一个数据包在网络中存活的最长时间,一般1-2分钟的样子;RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。

心跳包

如果对方异常断开,本机检测不到,一直等待,浪费资源
需要设置tcp的保持连接,作用就是每隔一定的时间间隔发送探测分节,如果连续发送多个探测分节对方还未回,就将次连接断开

int  keepAlive = 1;
setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));

心跳包: 最小粒度
乒乓包: 携带比较多的数据的心跳包

端口利用

int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

注意: 程序中设置某个端口重新使用,在这之前的其他网络程序将不能使用这个端口

posted @ 2021-09-18 20:25  wangzhilei  阅读(56)  评论(0编辑  收藏  举报