TCP 通信流程详解(附有案例代码)

简介: TCP 通信流程详解(附有案例代码)

一、TCP、UDP间的关系


TCP 和 UDP -> 传输层的协议:

UDP:用户数据报协议,面向无连接,可以单播,多播,广播, 面向数据报,不可靠

TCP:传输控制协议,面向连接的,可靠的,基于字节流,仅支持单播传输


                                               UDP                                                 TCP


是否创建连接                         无连接                                           面向连接


是否可靠                                不可靠                                             可靠的


连接的对象个数       一对一、一对多、多对一、多对多            支持一对一


传输的方式                         面向数据报                                     面向字节流


首部开销                               8个字节                                      最少20个字节


适用场景                 实时应用(视频会议,直播)         可靠性高的应用(文件传输)


二、TCP通信的流程


1、服务器端(被动接收连接的角色)


(1)创建一个用于监听的套接字


       - 监听:监听有客户端的连接


       - 套接字:这个套接字其实就是一个文件描述符


(2)将这个监听文件描述符和本地的IP和端口绑定(IP和端口就是服务器的地址信息)


       - 客户端连接服务器的时候使用的就是这个IP和端口


(3)设置监听,监听的fd开始工作


(4) 阻塞等待,当有客户端发起连接,解除阻塞,接受客户端的连接,会得到一个和客户端通信的套接字(fd)


(5)通信


       - 接收数据


       - 发送数据


(6)通信结束,断开连接  


2、客户端


(1)创建一个用于通信的套接字(fd)

(2)连接服务器,需要指定连接的服务器的 IP 和 端口

(3)连接成功了,客户端可以直接和服务器通信

       - 接收数据

       - 发送数据

(4) 通信结束,断开连接


3、TCP通信流程图


ae60370cb50c46d4845bb5f3cc09cd0b.png


三、套接字函数


#include <sys/types.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> // 包含了这个头文件,上面两个就可以省略 
int socket(int domain, int type, int protocol); 
- 功能:创建一个套接字 
- 参数:
    - domain: 协议族
        AF_INET : ipv4 
        AF_INET6 : ipv6 
        AF_UNIX, AF_LOCAL : 本地套接字通信(进程间通信) 
    - type: 通信过程中使用的协议类型 
        SOCK_STREAM : 流式协议 
        SOCK_DGRAM : 报式协议 
    - protocol : 具体的一个协议。一般写0 
        - SOCK_STREAM : 流式协议默认使用 TCP 
        - SOCK_DGRAM : 报式协议默认使用 UDP 
- 返回值: 
    - 成功:返回文件描述符,操作的就是内核缓冲区。 
    - 失败:-1 
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // socket命 名 
- 功能:绑定,将fd 和本地的IP + 端口进行绑定 
- 参数: 
    - sockfd : 通过socket函数得到的文件描述符 
    - addr : 需要绑定的socket地址,这个地址封装了ip和端口号的信息 - addrlen : 第二个参数结构体        
占的内存大小 
int listen(int sockfd, int backlog); // /proc/sys/net/core/somaxconn 
- 功能:监听这个socket上的连接 
- 参数:
    - sockfd : 通过socket()函数得到的文件描述符 
    - backlog : 未连接的和已经连接的和的最大值, 5 
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 
- 功能:接收客户端连接,默认是一个阻塞的函数,阻塞等待客户端连接 
- 参数: 
    - sockfd : 用于监听的文件描述符 
    - addr : 传出参数,记录了连接成功后客户端的地址信息(ip,port) 
    - addrlen : 指定第二个参数的对应的内存大小 - 返回值:- 成功 :用于通信的文件描述符 - -1 : 失败 
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 功能: 客户端连接服务器 
- 参数: 
    - sockfd : 用于通信的文件描述符 
    - addr : 客户端要连接的服务器的地址信息 
    - addrlen : 第二个参数的内存大小 
- 返回值:
    成功 0, 
    失败 -1 
ssize_t write(int fd, const void *buf, size_t count); // 写数据 
ssize_t read(int fd, void *buf, size_t count); // 读数据


四、TCP通信案例


1、服务器端


// TCP 通信的服务器端
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main() {
    // 1.创建socket(用于监听的套接字)
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd == -1) {
        perror("socket");
        exit(-1);
    }
    // 2.绑定(IP地址和端口)
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    // inet_pton(AF_INET, "192.168.193.128", saddr.sin_addr.s_addr);    //指定具体网卡
    saddr.sin_addr.s_addr = INADDR_ANY;  // 0.0.0.0 同时绑定多个网卡,使用哪个都可以
    saddr.sin_port = htons(9999);
    int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
    if(ret == -1) {
        perror("bind");
        exit(-1);
    }
    // 3.监听
    ret = listen(lfd, 8);
    if(ret == -1) {
        perror("listen");
        exit(-1);
    }
    // 4.接收客户端连接
    struct sockaddr_in clientaddr;
    int len = sizeof(clientaddr);
    int cfd = accept(lfd, (struct sockaddr *)&clientaddr, &len);
    if(cfd == -1) {
        perror("accept");
        exit(-1);
    }
    // 输出客户端的信息
    char clientIP[16];
    inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientIP, sizeof(clientIP));
    unsigned short clientPort = ntohs(clientaddr.sin_port);
    printf("client ip is %s, port is %d\n", clientIP, clientPort);
    // 5.通信
    char recvBuf[1024] = {0};
    while(1) {
        // 获取客户端的数据
        int num = read(cfd, recvBuf, sizeof(recvBuf));
        if(num == -1) {
            perror("read");
            exit(-1);
        } else if(num > 0) {
            printf("recv client data : %s\n", recvBuf);
        } else if(num == 0) {
            // 表示客户端断开连接
            printf("clinet closed...");
            break;
        }
        char * data = "hello,i am server";
        // 给客户端发送数据
        write(cfd, data, strlen(data));
    }
    // 关闭文件描述符
    close(cfd);
    close(lfd);
    return 0;
}


2、客户端


// TCP通信的客户端
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main() {
    // 1.创建套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        exit(-1);
    }
    // 2.连接服务器端
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    inet_pton(AF_INET, "192.168.193.128", &serveraddr.sin_addr.s_addr);
    serveraddr.sin_port = htons(9999);
    int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    if(ret == -1) {
        perror("connect");
        exit(-1);
    }
    // 3. 通信
    char recvBuf[1024] = {0};
    while(1) {
        char * data = "hello,i am client";
        // 给客户端发送数据
        write(fd, data , strlen(data));
        sleep(1);
        int len = read(fd, recvBuf, sizeof(recvBuf));
        if(len == -1) {
            perror("read");
            exit(-1);
        } else if(len > 0) {
            printf("recv server data : %s\n", recvBuf);
        } else if(len == 0) {
            // 表示服务器端断开连接
            printf("server closed...");
            break;
        }
    }
    // 关闭连接
    close(fd);
    return 0;
}

说明:在两个函数编译好后,在会话窗口1,先执行服务器端函数,他会阻塞等待;复制会话在新会话窗口执行客户端,开始通信;

相关文章
|
存储 网络协议
TCP服务器 IO多路复用的实现:select、poll、epoll
TCP服务器 IO多路复用的实现:select、poll、epoll
165 0
|
11月前
|
网络协议 Java
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
这篇文章全面讲解了基于Socket的TCP网络编程,包括Socket基本概念、TCP编程步骤、客户端和服务端的通信过程,并通过具体代码示例展示了客户端与服务端之间的数据通信。同时,还提供了多个案例分析,如客户端发送信息给服务端、客户端发送文件给服务端以及服务端保存文件并返回确认信息给客户端的场景。
一文讲明TCP网络编程、Socket套接字的讲解使用、网络编程案例
|
10月前
|
网络协议 C语言
C语言 网络编程(十一)TCP通信创建流程---服务端
在服务器流程中,新增了绑定IP地址与端口号、建立监听队列及接受连接并创建新文件描述符等步骤。`bind`函数用于绑定IP地址与端口,`listen`函数建立监听队列并设置监听状态,`accept`函数则接受连接请求并创建新的文件描述符用于数据传输。套接字状态包括关闭(CLOSED)、同步发送(SYN-SENT)、同步接收(SYN-RECEIVE)和已建立连接(ESTABLISHED)。示例代码展示了TCP服务端程序如何初始化socket、绑定地址、监听连接请求以及接收和发送数据。
|
8月前
|
网络协议 算法 网络性能优化
|
10月前
|
网络协议 C语言
C语言 网络编程(十四)并发的TCP服务端-以线程完成功能
这段代码实现了一个基于TCP协议的多线程服务器和客户端程序,服务器端通过为每个客户端创建独立的线程来处理并发请求,解决了粘包问题并支持不定长数据传输。服务器监听在IP地址`172.17.140.183`的`8080`端口上,接收客户端发来的数据,并将接收到的消息添加“-回传”后返回给客户端。客户端则可以循环输入并发送数据,同时接收服务器回传的信息。当输入“exit”时,客户端会结束与服务器的通信并关闭连接。
|
10月前
|
网络协议 C语言
C语言 网络编程(十)TCP通信创建流程---客户端
在TCP通信中,客户端需通过一系列步骤与服务器建立连接并进行数据传输。首先使用 `socket()` 函数创建一个流式套接字,然后通过 `connect()` 函数连接服务器。连接成功后,可以使用 `send()` 和 `recv()` 函数进行数据发送和接收。最后展示了一个完整的客户端示例代码,实现了与服务器的通信过程。
|
11月前
|
网络协议 安全 Unix
6! 用Python脚本演示TCP 服务器与客户端通信过程!
6! 用Python脚本演示TCP 服务器与客户端通信过程!
177 1
|
网络协议 算法 Linux
TCP教程:详解TCP连接过程
TCP教程:详解TCP连接过程
1193 0
|
网络协议 Java API
TCP/IP协议以及UDP(超详细,看这一篇就够了)
TCP/IP协议以及UDP(超详细,看这一篇就够了)
505 0
|
传感器 芯片
嵌入式通信协议全解析:SPI、I²C、UART详解(附带面试题)
通信是指人与人或人与自然之间通过某种行为或媒介进行的信息交流与传递。从广义上来说,通信是指需要信息的双方或多方在不违背各自意愿的情况下采用任意方法、任意媒质,将信息从某方准确安全地传送到另方。在出现电波传递通信后,通信被单一解释为信息的传递,是指由一地向另一地进行信息的传输与交换,其目的是传输消息。通信方式包括利用“电”来传递消息的电信,这种通信具有迅速、准确、可靠等特点,且几乎不受时间、地点、空间、距离的限制,因而得到了飞速发展和广泛应用。
2531 0