【Linux】TCP编程流程

TCP编程流程

【Linux】TCP编程流程_第1张图片

socket()创建套接字,套接字TCP协议选择流式服务SOCK_STREAM。

bind()指定套接字使用的IP地址和端口。IP地址是自己主机地址,端口为一个16位的整形值。

listen()方法创建监听队列。监听队列分为存放未完成三次握手的连接和完成三次握手的连接。其第二个参数位指定已完成三次握手队列的长度。

accept()处理存放在listen创建的已完成三次握手的队列中的连接,如果队列为空可能阻塞。

connect()有客户端程序执行建立连接,进行三次握手,指定连接的服务器IP地址和端口

send()向TCP连接的对端发送数据。

recv()用接收TCP连接的对端发送来的数据,返回值为0说明对方已经关闭连接。

close()关闭TCP连接。


TCP有关概念

TCP协议:面向连接可靠流式服务。(传输层)

TCP三次握手建立连接,四次挥手断开连接。

【Linux】TCP编程流程_第2张图片

TCP 的可靠传输是通过使用应答确认超时重传来完成

【Linux】TCP编程流程_第3张图片

可靠性:牺牲一定开销,应答确认、超时重传、去重(序号相同的丢弃)、乱序重排。

滑动窗口:流量控制(控制发送的数据量,太慢效率低,太快缓冲区满)

【Linux】TCP编程流程_第4张图片

 粘包:连续send()时,recv()的次数比send()少,无法准确分割出发送方要表达的意义。

       解决:告诉大小,设置特殊符号分割,不连续send

【Linux】TCP编程流程_第5张图片

字节序列:大端、小端

大端:网络字节序列(整形必须转成大端)

#include 
uint32_t htonl(uint32_t hostlong); // 长整型的主机字节序转网络字节序
uint32_t ntohl(uint32_t netlong); // 长整型的网络字节序转主机字节序
uint16_t htons(uint16_t hostshort); // 短整形的主机字节序转网络字节序
uint16_t ntohs(uint16_t netshort); // 短整型的网络字节序转主机字节序

 TCP服务端代码(多线程并发)

        服务端接受一个客户端的连接后,创建一个线程或者进程,然后在新创建的线程或进程中循环处理数据。主线程(父进程)只负责监听客户端的连接,并使用 accept()接受连接,不进行数据的处理。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

//多线程并发
struct Node_Arg
{
    int c;
};

void* fun(void *arg)
{
    struct Node_Arg* p = (struct Node_Arg*)arg;
    int c = p->c;

    while( 1 )
    {
        char buff[128] = {0};
        int num = recv(c,buff,127,0);
        if ( num <= 0 )
        {
            break;
        }

        printf("buff(c=%d)=%s\n",c,buff);
        send(c,"ok",2,0);
    }
    close(c);
    free(p);
    printf("client close\n");
}
int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建套接字
    if ( sockfd == -1)
    {
        exit(1);
    }

    struct sockaddr_in saddr,caddr;//服务器地址、客户端地址
    memset(&saddr,0,sizeof(saddr));//清空服务器地址
    saddr.sin_family = AF_INET;//地址簇
    saddr.sin_port = htons(6000);
//htons 将主机字节序转换为网络字节序: 1024 知名端口 , 4096保留, 临时端口
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//绑定端口、ip地址
    if ( res == -1)
    {
        printf("bind err\n");
        exit(1);
    }

    res = listen(sockfd,5);
    if ( res == -1)
    {
        exit(1);
    }

    while( 1 )
    {
        int len = sizeof(caddr);
        int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//阻塞
        if ( c < 0 )
        {
            continue;
        }

        printf("c=%d\n",c);

        pthread_t id;
        struct Node_Arg * ptr = (struct Node_Arg*)malloc(sizeof(struct Node_Arg));
        ptr->c = c;
        pthread_create(&id,NULL,fun,ptr);//创建线程
    }
    close(sockfd);
    exit(0);
}

TCP客户端代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
int main()
{
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); // SOCK_STREAM是tcp的
    if (sockfd == -1)
        exit(1);

    struct sockaddr_in saddr;         // 服务器地址
    memset(&saddr, 0, sizeof(saddr)); // 清空,(有一个占位的)
    saddr.sin_family = AF_INET;       // 地址族,告诉它用的什么协议ipv4,ipv6
    saddr.sin_port = htons(6000);     // 端口,进程代号,htons短整形的主机字节序转网络字节序
    // 设置服务器地址
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//哪个主机
    // 连接服务器
    int res = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
    if (res == -1)
    {
        printf("connect err\n");
        exit(1);
    };

    while (1)
    {
        printf("input:\n");
        char buff[128] = {0};
        fgets(buff, 128, stdin);

        if (strncmp(buff, "end", 3) == 0)
            break;

        send(sockfd, buff, strlen(buff), 0);//发送到发送缓冲区
        memset(buff, 0, sizeof(buff)); // 清空
        
        //接受数据
        recv(sockfd, buff, 127, 0);//看接收缓冲区(全双工)
        printf("buff = %s\n", buff);
    }

    close(sockfd);
    exit(0);
}

【Linux】TCP编程流程_第6张图片

你可能感兴趣的:(Linux,linux,tcp/ip,网络)