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,先执行服务器端函数,他会阻塞等待;复制会话在新会话窗口执行客户端,开始通信;

相关文章
|
2月前
|
运维 监控 网络协议
QT实现TCP通信:从基础到高级的全面解析
QT实现TCP通信:从基础到高级的全面解析
206 0
|
5月前
|
网络协议 安全 数据库
C++实战项目-网络编程基础包含TCP详解(一)
C++实战项目-网络编程基础包含TCP详解(一)
82 0
|
3月前
|
网络协议
计网 - TCP三次握手原理全曝光:深度解析与实战演示
计网 - TCP三次握手原理全曝光:深度解析与实战演示
45 0
|
3月前
|
网络协议
计网 - TCP四次挥手原理全曝光:深度解析与实战演示
计网 - TCP四次挥手原理全曝光:深度解析与实战演示
63 0
|
9月前
|
网络协议
TCP协议补充实验(二)
TCP协议补充实验
45 0
|
5月前
|
缓存 网络协议 API
C++实战项目-网络编程基础包含TCP详解(二)
C++实战项目-网络编程基础包含TCP详解(二)
105 0
|
6月前
|
网络协议 网络性能优化 数据库
华为18级工程师用200张图带你一次性搞懂HTTP/TCP/IP网络协议
互联网世界中,网络协议的重要性不言而喻。很多人都知道,网络协议中的五层模型或者七层模型,这些在操作系统中,那都是“必考题”。上学的时候,无论是死记硬背,还是各种小抄,总得把下面这个图记下来。踏入工作,走进 web 开发“不归路”,发现还是不能落下它。 计算机网络相关的知识点是在面试过程中开发者经常被问到。当然可能这一块知识点与前面的操作系统、数据库相比较比重可能没那么高。但是优秀的你,一定是想做好充分的准备吧!
|
9月前
|
运维 网络协议 应用服务中间件
C/S UDP通信实践踩坑记录与对于ICMP的进一步认识
C/S UDP通信实践踩坑记录与对于ICMP的进一步认识
|
9月前
|
监控 网络协议
TCP协议补充实验(一)
TCP协议补充实验
55 0
|
10月前
|
网络协议
TCP 通信并发服务器详解(附有案例代码)
TCP 通信并发服务器详解(附有案例代码)