当前位置: 首页 > news >正文

《TCP多线程通信代码C语言开发流程解析》

TCP多线程通信代码开发流程解析——让步骤清晰明了

在编写 TCP 通信代码时,我经常中途忘记需要实现哪些步骤。为了更好地熟悉整个流程,并提高今后的开发效率,我打算自己写一篇文章,系统地整理并总结编写 TCP 通信所需的关键代码和步骤。

服务端的流程

服务端通过主线程接受客户端连接,并为每个连接创建独立会话线程(管理线程),在会话线程中再启两个子线程分别负责读和写。
在这里插入图片描述


1.接收客户端并为其分配线程

  1. 创建sockaddr_in结构体及socket套接字

    struct sockaddr_in server_addr;
    memset(&server_addr,0,sizeof(server_addr));
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(port);
    int len_server_addr=sizeof(server_addr);//结构体的大小
    //套接字
    int serverfd=socket(AF_INET,SOCK_STREAM,0);
    
  2. bind绑定

    
    temp_result=bind(serverfd,(struct sockaddr*)&server_addr,len_server_addr);if(temp_result){handle_error("bind",temp_result);}
    
  3. listen监听

    temp_result=listen(serverfd,10);//第二个参数是最多可以接受多少个客户端连接请求if(temp_result){handle_error("listen",temp_result);}
    
  4. 创建线程accept连接

    关键点

    • pthread_t fd; 只是个变量。
    • 每次调用 pthread_create,都会把新建线程的 ID 写到 fd 里。
    • 你紧接着又 pthread_detach(fd),把这个线程设置为**“分离态”**,这样它结束时系统能自动回收资源。
    • 下一次循环时pthread_create 会再次把新的线程 ID 写进 fd(覆盖掉旧的)。
    pthread_t fd;
    struct sockaddr_in client_addr;
    memset(&client_addr,0,sizeof(client_addr));
    int client_addr_len=sizeof(client_addr);
    while (1)
    {int *client_fd=malloc(sizeof(int));*client_fd=accept(serverfd,(struct sockaddr*)&client_addr,&client_addr_len);printf("客户端%d,ip:%s,已成功与服务端连接\n",*client_fd,inet_ntoa(client_addr.sin_addr));pthread_create(&fd,NULL,&raw,client_fd);pthread_detach(fd);
    }
    //释放资源
    close(serverfd);
    

2.管理该客户端的读写子线程

为什么要传输文件描述符,这就相当于一个通道,没有他我们就找不到目标地址,也就无法进行读写操作,这就相当于一个通讯大门

void *read_func(void *arg)
{int server_fd = *(int *)arg;char *read_buf = malloc(1024);int temp_result;while (1){temp_result = recv(server_fd, read_buf, 1024, 0);if (temp_result == -1){perror("recv");}else if (temp_result == 0){printf("客户端关闭了连接\n");break;}read_buf[temp_result] = '\0'; // 安全添加结束符,防止乱码printf("客户端:%s\n", read_buf);}free(read_buf);return NULL;
}void *write_func(void *arg)
{int server_fd = *(int *)arg;char *write_buf = malloc(1024);int temp_result;while (1){memset(write_buf, 0, 1024);if (fgets(write_buf, 1024, stdin) == NULL){printf("输入有误\n");break;}if (strncmp(write_buf, "exit", 4) == 0){printf("服务端主动退出\n");break;}temp_result = send(server_fd, write_buf, strlen(write_buf), 0);if (temp_result == -1){perror("send");}else if (temp_result == 0){printf("服务器关闭了连接\n");break;}else{printf("服务端:%s\n", write_buf);}}free(write_buf);return NULL;
}void *raw(void * argv)
{int sockfd=*(int*)argv;pthread_t read_t,write_t;//创建子线程读取数据并打印到终端pthread_create(&read_t, NULL, read_func, argv);//创建子线程写入数据传到服务端pthread_create(&write_t, NULL, write_func, argv);// 主线程等待子线程退出pthread_join(read_t, NULL);pthread_join(write_t, NULL);free(argv);printf("关闭资源\n");close(sockfd);
}

3.完整代码展示

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>#define port 6666#define handle_error(cmd, result) \if (result < 0)               \{                             \perror(cmd);              \return -1;                \}void *read_func(void *arg)
{int server_fd = *(int *)arg;char *read_buf = malloc(1024);int temp_result;while (1){temp_result = recv(server_fd, read_buf, 1024, 0);if (temp_result == -1){perror("recv");}else if (temp_result == 0){printf("客户端关闭了连接\n");break;}read_buf[temp_result] = '\0'; // 安全添加结束符,防止乱码printf("客户端:%s\n", read_buf);}free(read_buf);return NULL;
}void *write_func(void *arg)
{int server_fd = *(int *)arg;char *write_buf = malloc(1024);int temp_result;while (1){memset(write_buf, 0, 1024);if (fgets(write_buf, 1024, stdin) == NULL){printf("输入有误\n");break;}if (strncmp(write_buf, "exit", 4) == 0){printf("服务端主动退出\n");break;}temp_result = send(server_fd, write_buf, strlen(write_buf), 0);if (temp_result == -1){perror("send");}else if (temp_result == 0){printf("服务器关闭了连接\n");break;}else{printf("服务端:%s\n", write_buf);}}free(write_buf);return NULL;
}void *raw(void * argv)
{int sockfd=*(int*)argv;pthread_t read_t,write_t;//创建子线程读取数据并打印到终端pthread_create(&read_t, NULL, read_func, argv);//创建子线程写入数据传到服务端pthread_create(&write_t, NULL, write_func, argv);// 主线程等待子线程退出pthread_join(read_t, NULL);pthread_join(write_t, NULL);free(argv);printf("关闭资源\n");close(sockfd);
}int main(int argc, char const *argv[])
{int temp_result;//声明server结构体以及初始化struct sockaddr_in server_addr;memset(&server_addr,0,sizeof(server_addr));server_addr.sin_addr.s_addr=htonl(INADDR_ANY);server_addr.sin_family=AF_INET;server_addr.sin_port=htons(port);int len_server_addr=sizeof(server_addr);//套接字int serverfd=socket(AF_INET,SOCK_STREAM,0);//绑定temp_result=bind(serverfd,(struct sockaddr*)&server_addr,len_server_addr);if(temp_result){handle_error("bind",temp_result);}//监听temp_result=listen(serverfd,10);if(temp_result){handle_error("listen",temp_result);}//接收pthread_t fd;struct sockaddr_in client_addr;memset(&client_addr,0,sizeof(client_addr));int client_addr_len=sizeof(client_addr);while (1){int *client_fd=malloc(sizeof(int));*client_fd=accept(serverfd,(struct sockaddr*)&client_addr,&client_addr_len);printf("客户端%d,ip:%s,已成功与服务端连接\n",*client_fd,inet_ntoa(client_addr.sin_addr));pthread_create(&fd,NULL,&raw,client_fd);pthread_detach(fd);}//释放资源close(serverfd);return 0;
}

客户端的流程

  1. 创建sockaddr_in初始化
  2. connect连接

1.连接服务端

int main(int argc, char const *argv[])
{// 套接字int client_fd = socket(AF_INET, SOCK_STREAM, 0);// 初始化struct sockaddr_in client_addr;memset(&client_addr, 0, sizeof(client_addr));client_addr.sin_addr.s_addr = inet_addr("127.0.0.1");client_addr.sin_family = AF_INET;client_addr.sin_port = htons(6666);int client_addr_len = sizeof(client_addr);int sockfd = connect(client_fd, (struct sockaddr *)&client_addr, client_addr_len);if (sockfd == -1){handle_error("connect", sockfd);}pthread_t read_t, write_t;// 创建子线程读取数据并打印到终端pthread_create(&read_t, NULL, &read_func, (void *)&client_fd);// 创建子线程写入数据传到服务端pthread_create(&write_t, NULL, &write_func, (void *)&client_fd);// 主线程等待子线程退出pthread_join(read_t, NULL);pthread_join(write_t, NULL);printf("关闭资源\n");close(sockfd);return 0;
}

2.客户端的读写子线程

和服务端实现方法一样

void *read_func(void *arg)
{int server_fd = *(int *)arg;char *read_buf = malloc(1024);int temp_result;while (1){temp_result = recv(server_fd, read_buf, 1024, 0);if (temp_result == -1){perror("recv");}else if (temp_result == 0){printf("服务器关闭了连接\n");break;}read_buf[temp_result] = '\0'; // 安全添加结束符,防止乱码printf("服务器:%s\n", read_buf);}free(read_buf);return NULL;
}void *write_func(void *arg)
{int server_fd = *(int *)arg;char *write_buf = malloc(1024);int temp_result;while (1){memset(write_buf, 0, 1024);if (fgets(write_buf, 1024, stdin) == NULL){printf("输入有误\n");break;}if (strncmp(write_buf, "exit", 4) == 0){printf("客户端主动退出\n");break;}temp_result = send(server_fd, write_buf, strlen(write_buf), 0);if (temp_result == -1){perror("send");}else if (temp_result == 0){printf("服务器关闭了连接\n");break;}else{printf("客户端:%s\n", write_buf);}}free(write_buf);return NULL;
}

3.完整代码

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>#define handle_error(cmd, result) \if (result < 0)               \{                             \perror(cmd);              \return -1;                \}void *read_func(void *arg)
{int server_fd = *(int *)arg;char *read_buf = malloc(1024);int temp_result;while (1){temp_result = recv(server_fd, read_buf, 1024, 0);if (temp_result == -1){perror("recv");}else if (temp_result == 0){printf("服务器关闭了连接\n");break;}read_buf[temp_result] = '\0'; // 安全添加结束符,防止乱码printf("服务器:%s\n", read_buf);}free(read_buf);return NULL;
}void *write_func(void *arg)
{int server_fd = *(int *)arg;char *write_buf = malloc(1024);int temp_result;while (1){memset(write_buf, 0, 1024);if (fgets(write_buf, 1024, stdin) == NULL){printf("输入有误\n");break;}if (strncmp(write_buf, "exit", 4) == 0){printf("客户端主动退出\n");break;}temp_result = send(server_fd, write_buf, strlen(write_buf), 0);if (temp_result == -1){perror("send");}else if (temp_result == 0){printf("服务器关闭了连接\n");break;}else{printf("客户端:%s\n", write_buf);}}free(write_buf);return NULL;
}int main(int argc, char const *argv[])
{// 套接字int client_fd = socket(AF_INET, SOCK_STREAM, 0);// 初始化struct sockaddr_in client_addr;memset(&client_addr, 0, sizeof(client_addr));client_addr.sin_addr.s_addr = inet_addr("127.0.0.1");client_addr.sin_family = AF_INET;client_addr.sin_port = htons(6666);int client_addr_len = sizeof(client_addr);int sockfd = connect(client_fd, (struct sockaddr *)&client_addr, client_addr_len);if (sockfd == -1){handle_error("connect", sockfd);}pthread_t read_t, write_t;// 创建子线程读取数据并打印到终端pthread_create(&read_t, NULL, &read_func, (void *)&client_fd);// 创建子线程写入数据传到服务端pthread_create(&write_t, NULL, &write_func, (void *)&client_fd);// 主线程等待子线程退出pthread_join(read_t, NULL);pthread_join(write_t, NULL);printf("关闭资源\n");close(sockfd);return 0;
}

总结

TCP 多线程服务端的实现,看似复杂,但本质就是“主线程接收连接、子线程处理读写”。掌握文件描述符和线程管理,你就掌握了网络通信的核心钥匙。希望本文能帮助你对网络编程有更直观的理解。

http://www.dtcms.com/a/347526.html

相关文章:

  • redis----hash类型详解
  • 领码方案:新一代页面权限体系全景解析(完整版)
  • Radis安装部署(Linux,Docker)
  • 温度对直线导轨的性能有哪些影响?
  • TypeScript 的泛型(Generics)作用理解
  • 如何优雅解决 OpenCV 分段错误(Segfault):子进程隔离实战
  • 工业企业与海关匹配数据(2000-2013)
  • Unity中删除不及时的问题
  • DeepSeek-V3.1发布,预示下一代国产芯片即将发布,更新一小版本,跨出一大步
  • 深入理解3x3矩阵
  • Java—— 配置文件Properties
  • Spring Boot 实现 POJO 级联封装复杂属性
  • Redis学习笔记 ----- 缓存
  • 寻鲜之旅“咖”约深圳,容声冰箱引领“养鲜”新体验
  • 解决coze api使用coze.workflows.runs.create运行workflow返回400,但text为空
  • ⚡ Ranger 基础命令与功能详解
  • Talkie AI
  • 硬件笔记(27)---- 恒流源电路原理
  • 环境 (shell) 变量
  • QT-Mysql-查询语句-查询是否有表-表列名-查询记录
  • 力扣hot100:搜索二维矩阵与在排序数组中查找元素的第一个和最后一个位置(74,34)
  • ros 消息类型与查阅相关内容
  • XCVM1802-2MSEVSVA2197 XilinxAMD Versal Premium FPGA
  • 同步和异步、阻塞和非阻塞的再理解
  • JAVA核心基础篇-集合
  • 力扣(组合)
  • 如何解决 pyqt5 程序“长时间运行失效” 问题?
  • React学习(十一)
  • Windows 平台查看端口占用情况并终止进程
  • flink常见问题之非法配置异常