Multi-Thread TCP Server Client

news/2025/1/15 22:46:04/

prerequisite knowledge:
Basic TCP Server & Client: URL

Server

#include <stdio.h>
#include <string.h>
#include <unistd.h> // read and write (TCP); sendto and recvfrom (UDP)
#include <arpa/inet.h> // 包含#include <sys/socket.h>
#include <pthread.h>// 想要将两份参数通过一个位置传递给working 得定义一个信息结构体
struct SockInfo {struct sockaddr_in addr;int fd;
};// 由于有多个客户端 所以有多份信息 搞个数组存一下
struct SockInfo infos[512]; // 意味着我们最多只能和512个客户端*同时*通信
// 注意线程池是多个工作线程组成的队列,与此处不同/* infos需要上锁吗?不需要,每个子线程都使用了数组的一个位置,即多个线程没有操控同一内存空间*/void* working(void* arg) {struct SockInfo* pinfo = (struct SockInfo*)arg;printf("client socket %d, Address: %s:%d\n", pinfo->fd, inet_ntoa(pinfo->addr.sin_addr), ntohs(pinfo->addr.sin_port));// 5. 通信while (1) {char buf[1024];int len = recv(pinfo->fd, buf, sizeof(buf), 0);if (len > 0) {printf("client say: %s\n", buf);send(pinfo->fd, buf, len, 0); // 长度指定为len 不要传多了} else if (len == 0) {printf("客户端已经断开连接...\n");break;} else if (len == -1) {perror("recv");break;}} // 跳出后说明通信结束pinfo->fd = -1; // 回收close(pinfo->fd);return NULL;
}int main(int argc, char* argv[]) {// 1. 创建监听fdint fd = socket(PF_INET, SOCK_STREAM, 0);if (fd == -1) {perror("socket");return -1;}// 2. 绑定监听fdstruct sockaddr_in saddr;memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET;saddr.sin_port = htons(9999);saddr.sin_addr.s_addr = INADDR_ANY; // 宏INADDR_ANY(可以绑定本地)实际值是0=0.0.0.0;由于大小端没区别,因此无需htonlint ret = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));if (ret == -1) {perror("bind");return -1;}// 3. 设置监听ret = listen(fd, SOMAXCONN); // #define SOMAXCONN 128 // 最大监听队列长度 内部定义过来if (ret == -1) {perror("listen");return -1;}// 初始化信息数组int max = sizeof(infos) / sizeof(infos[0]);for (int i=0; i<max; ++i) {memset(&infos[i], 0, sizeof(infos[i]));infos[i].fd = -1; // -1表示该数组元素未被占用}// 4. 阻塞并等待客户端连接// struct sockaddr_in caddr; // 这个就不需要了// memset(&caddr, 0, sizeof(caddr));socklen_t caddr_len = sizeof(struct sockaddr_in);// 主线程不断接收:while (1) {// 创建子线程pthread_t tid;struct SockInfo* pinfo;for (int i=0; i<max; ++i) {if (infos[i].fd == -1) {pinfo = &infos[i];break;}}int cfd = accept(fd, (struct sockaddr*)&pinfo->addr, &caddr_len); // 直接传入信息数组中空闲元素的addrpinfo->fd = cfd; // 传递cfdif (cfd == -1) {perror("accept");break;}pthread_create(&tid, NULL, working, pinfo);// 此处不能使用主线程回收子线程资源 因为pthread_join是阻塞函数 与我们设想的不断accept矛盾pthread_detach(tid);}close(fd);return 0;
}

Client

#include <stdio.h>
#include <string.h>
#include <unistd.h> // read and write (TCP); sendto and recvfrom (UDP)
#include <arpa/inet.h> // 包含#include <sys/socket.h>int main(int argc, char* argv[]) {// 1. 创建通信fdint fd = socket(PF_INET, SOCK_STREAM, 0); // AF_*和PF_*值完全相同,通常混用if (fd == -1) {perror("socket");return -1;}// 2. 连接服务器struct sockaddr_in saddr;memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET;saddr.sin_port = htons(9999);inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr.s_addr);saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 或者直接指定ip: 172.31.78.11int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));if (ret == -1) {perror("connect");return -1;}printf("socket connect successful!\n");// 3. 通信int number = 0;while (1) {char buf[1024];sprintf(buf, "hello, message number #%d...\n", number++); // sprintf将数据写入字符串 而非输出到标准输出流send(fd, buf, strlen(buf)+1, 0); // 注意这里不要发送sizeof(buf),发送实际字符数+'\0'memset(buf, 0, sizeof(buf)); // 有必要清空buf的int len = recv(fd, buf, sizeof(buf), 0);if (len > 0) {printf("server say: %s\n", buf);} else if (len == 0) {printf("服务器已经断开连接...\n");break;} else if (len == -1) {perror("recv");break;}sleep(1); // 让客户端1秒发一条} // 跳出后说明通信结束close(fd);return 0;
}

makefile

server:gcc -o server server.c -lpthread &gcc -o client client.cclean:rm -f server client

http://www.ppmy.cn/news/1450742.html

相关文章

TCP协议在物联网中的实战

一、TCP协议介绍 网上对TCP协议介绍众多&#xff0c;本人按照自己的理解简单介绍一下。 TCP&#xff08;Transmission Control Protocol&#xff0c; 传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输控制层通信协议。 1.1 协议机制 1.1.1 三次握手 &…

线上线下包搭建小程序/公众号/H5 支持二开!

网上交友有以下三个积极影响&#xff1a; 1. 扩展社交圈和增加社交机会&#xff1a;网上交友可以让人们接触到不同地区、不同背景、不同文化的人&#xff0c;拓展人们的社交圈并且增加交友机会。这些新的社交联系对于个人的成长和发展有积极的影响&#xff0c;可以让人们学习新…

SpringBoot 打包所有依赖

SpringBoot 项目打包的时候可以通过插件 spring-boot-maven-plugin 来 repackage 项目&#xff0c;使得打的包中包含所有依赖&#xff0c;可以直接运行。例如&#xff1a; <plugins><plugin><groupId>org.springframework.boot</groupId><artifact…

线性神经网络示例

通过5个条件判定一件事情是否会发生&#xff0c;5个条件对这件事情是否发生的影响力不同&#xff0c;计算每个条件对这件事情发生的影响力多大&#xff0c;写一个线性神经网络模型pytorch程序,最后打印5个条件分别的影响力。 一 在这个场景中&#xff0c;一个线性神经网络&…

什么是 IoT,代表性的 IoT 产品或服务都有哪些?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 物联网&#xff08;IoT&#xff09;是一个由互联设备组成的网络&#xff0c;包括嵌入传感器、软件等技术的机械和数字机器&#xff0c;以及消费品。这些设备能够相互连接&#xff0c;并与云交换数据。I…

杨辉三角(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int a[10][10] { 0 };int i, j;//赋值&#xff1b;a[0][0] 1;//实现杨辉三角&#xff1b;…

zookeeper数据迁移至clickhouse-keeper

1&#xff09;找到zookeeper主节点 zkServer.sh status 查看返回结果&#xff1a; ZooKeeper JMX enabled by default Using config: /conf/zoo.cfg Client port found: 2181. Client address: localhost. Client SSL: false.Mode: leader Mode说明&#xff1a;值为leader 代表…

【深度学习实战(30)】训练框架之使用tensorboard记录loss

一、 安装Tensorboard库 pip install tensorflow pip install tensorboardx二、LossHistory类实现过程 1. init构造函数 传入参数log保存路径&#xff0c;模型&#xff0c;模型输入尺寸 def __init__(self, log_dir, model, input_shape):实例化SummaryWriter对象 self.wr…