12_TCP和UDP实现服务端和客户端的通信

news/2025/9/26 11:09:59/文章来源:https://www.cnblogs.com/z1yang/p/19113109

一、TCP实现多进程通信

server_process.c 服务端代码:

#define _XOPEN_SOURCE
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <wait.h>
#include <errno.h>
void recyleChild(int arg)
{while(1){int ret=waitpid(-1,NULL,WNOHANG);if(ret==-1){//所有的子进程都被回收了;break;}else if(ret==0){//还有子进程活着;break;}else if(ret>0){//被回收了printf("子进程%d被回收了\n",ret);}}}int main()
{//注册信号捕捉struct sigaction act;act.sa_flags=0;sigemptyset(&act.sa_mask);act.sa_handler=recyleChild;sigaction(SIGCHLD,&act,NULL);//创建socketint lfd =socket(AF_INET,SOCK_STREAM,0);if(lfd==-1){perror("socket");exit(-1);}//绑定struct sockaddr_in saddr;saddr.sin_addr.s_addr=0;saddr.sin_port=htons(9999);saddr.sin_family=AF_INET;int ret = bind(lfd,(const struct sockaddr*)&saddr,sizeof(saddr));if(ret==-1){perror("bind");exit(-1);}//监听ret=listen(lfd,8);if(ret==-1){perror("listen");exit(-1);}//不断循环,接收客户端连接;while(1){struct sockaddr_in cliaddr;int len =sizeof(cliaddr);//接收连接int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);if(cfd==-1){if(errno==EINTR){continue;}perror("accept");exit(-1);}//每一个连接进来就创建一个子进程进行客户端通信;pid_t pid=fork();if(pid==0){//子进程//获取客户端信息char cliIP[16];inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,cliIP,sizeof(cliIP));unsigned short cliPort=ntohs(cliaddr.sin_port);printf("cliIp is :%s,port is %d\n",cliIP,cliPort);//接收客户端发来的数据char recvBuff[1024]={0};while(1){int len1=read(cfd,&(recvBuff),sizeof(recvBuff)+1);if(len1==-1){perror("read");exit(-1);}else if(len1>0){printf("recive client data:%s\n",recvBuff);}else if(len1==0){printf("client close\n");}write(cfd,recvBuff,strlen(recvBuff)+1);}close(cfd);exit(0);}}close(lfd);return 0;
}

分析:

  1. 首先创建socket函数,在进行绑定,封装服务器的IP、端口号等相关信息;
  2. 然后进行监听,等待客户端的连接,客户端的连接是在while循环中进行的,因为需要不断接收客户端的连接;
  3. 连接成功一个客户端后,创建一个进程,在子进程完成服务器与客户端的通信;
  4. 关于子进程的回收是利用信号进行回收,因为如果利用wait函数进行回收,会造成程序在对应位置进行阻塞。

client.c客户端代码 :

//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 serve_addr;serve_addr.sin_family=AF_INET;inet_pton(AF_INET,"192.168.254.171",&serve_addr.sin_addr.s_addr);serve_addr.sin_port=htons(9999);int ret= connect(fd,(const struct sockaddr *)&serve_addr,sizeof(serve_addr));if(ret==-1){perror("connect");exit(-1);}//3、进行通信char readbuff[1024]={0};// char writebuff[1024]={0};int i=0;while(1){// memset(writebuff,0,sizeof(writebuff));//printf("请输入内容:\n");//scanf("%s",writebuff);//char * data="I am client";sprintf(readbuff,"data:%d\n",i++);//给服务端发送数据write(fd, &readbuff,strlen(readbuff));sleep(1);int len =read(fd,readbuff,sizeof(readbuff));if(len==-1){perror("read");exit(-1);}else if(len>0){printf("recive client data:%s\n",readbuff);}else if(len==0){//表示服务器端断开连接。printf("serve closed...\n");break;}}//关闭文件描述符;close(fd);return 0;
}

 分析:

  1. 首先创建socket函数,然后连接服务器;
  2. 链接成功后进行通信,进行数据的收发;
  3. 最后回收文件描述符。
  4. 一个客户端在一个子进程中通信,这也是TCP实现多进程通信的方法原理。

 二、TCP实现多线程通信 

利用TCP实现多线程通信与多进程通信的区别在于,当服务端连接到客户端后,与每一个客户端进行通信时,是创建进程进行通信函数还是线程进行通信函数,也就是说客户端的代码是相同的,区别在于服务器端的代码。

server_process.c 服务端代码:

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>struct sockinfo//该结构体用于存放服务端发送给子进程关于客户端的信息,因为进程只能传递一个参数。
{int fd;//通信文件描述符;struct sockaddr_in addr;pthread_t tid;//线程号;
};struct sockinfo sockinfos[128];//定义全局变量,用于存放客户端的信息;void * working (void *arg)
{
//获取客户端信息struct sockinfo *pinfo=(struct sockinfo*)arg;char cliIP[16];inet_ntop(AF_INET,&pinfo->addr.sin_addr.s_addr,cliIP,sizeof(cliIP));unsigned short cliPort=ntohs(pinfo->addr.sin_port);printf("cliIp is :%s,port is %d\n",cliIP,cliPort);//接收客户端发来的数据char recvBuff[1024]={0};while(1){int len1=read(pinfo->fd,&(recvBuff),sizeof(recvBuff)+1);if(len1==-1){perror("read");exit(-1);}else if(len1>0){printf("recive client data:%s\n",recvBuff);}else if(len1==0){printf("client close\n");}write(pinfo->fd,recvBuff,strlen(recvBuff)+1);}close(pinfo->fd);exit(0);
//子线程与客户端进行通信; cfd; 客户端的信息;线程号;return NULL;
}int main()
{//创建socketint lfd =socket(AF_INET,SOCK_STREAM,0);if(lfd==-1){perror("socket");exit(-1);}//绑定struct sockaddr_in saddr;saddr.sin_addr.s_addr=0;saddr.sin_port=htons(9999);saddr.sin_family=AF_INET;int ret = bind(lfd,(const struct sockaddr*)&saddr,sizeof(saddr));if(ret==-1){perror("bind");exit(-1);}//监听ret=listen(lfd,8);if(ret==-1){perror("listen");exit(-1);}//  初始化数据int max = sizeof(sockinfos)/sizeof( sockinfos[0]);for(int i=0;i<max;i++){bzero(&sockinfos[i],sizeof(sockinfos[i]));sockinfos[i].fd=-1;sockinfos[i].tid=-1;}while(1){struct sockaddr_in cliaddr;int len =sizeof(cliaddr);//接收连接int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);struct sockinfo *pinfo;for(int i=0;i<max;i++){//从这个数组中可以找到一个可以用的sockInfo元素;if (sockinfos[i].fd==-1){pinfo=&sockinfos[i];break;}if(i==max-1){sleep(1);i--;}}pinfo->fd=cfd;memcpy(&pinfo->addr,&cliaddr,len);pthread_t tid;//  每一个连接进来,创建一个子线程与客户端进行通信pthread_create(&pinfo->tid,NULL,working,pinfo);pthread_detach(pinfo->tid);}close(lfd);return 0;
}

三、UDP实现通信

server_udp.c:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define SERVER_PORT 9999int server_udp()
{int ret = 0;int socket_fd = -1;int addr_len = 0;char buf[1024] = {0};struct sockaddr_in client_addr;struct sockaddr_in server_addr;socket_fd = socket(AF_INET, SOCK_DGRAM, 0);if (socket_fd < 0) {printf("%s: socket failed\n", __FUNCTION__);return 0;}memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = htonl(INADDR_ANY);addr_len = sizeof(server_addr);ret = bind(socket_fd, (const struct sockaddr *)&server_addr, addr_len);if (ret < 0) {printf("%s: bind failed\n", __FUNCTION__);close(socket_fd);return 0;}while (1) {memset(buf, 0, sizeof(buf));addr_len = sizeof(client_addr);ret = recvfrom(socket_fd, buf, sizeof(buf), 0,(struct sockaddr *)&client_addr, &addr_len);if (ret > 0) {printf("recv len:%d, data:[%s] from %s:%d\n", ret, buf,inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));} else if (0 == ret) {printf("ret:%d\n", ret);} else {printf("ret:%d\n", ret);}}close(socket_fd);return 0;
}int main(int argc, char *argv[])
{server_udp();return 0;
}

client_udp.c:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 9999int client_udp(char *data)
{int ret = 0;int socket_fd = -1;int addr_len = 0;struct sockaddr_in client_addr;struct sockaddr_in server_addr;socket_fd = socket(AF_INET, SOCK_DGRAM, 0);if (socket_fd < 0) {printf("%s: socket failed\n", __FUNCTION__);return 0;}memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);addr_len = sizeof(server_addr);ret = sendto(socket_fd, data, strlen(data), 0,(struct sockaddr *)&server_addr, addr_len);if (ret > 0) {printf("send data: [%s] to %s:%d\n", data, inet_ntoa(server_addr.sin_addr),ntohs(server_addr.sin_port));} else {printf("ret:%d\n", ret);}close(socket_fd);return 0;
}int main(int argc, char *argv[])
{if (argc < 2) {client_udp("hello world");} else {client_udp(argv[1]);}return 0;
}

Makefile:

all:gcc -o server server_udp.cgcc -o client client_udp.c
clean:-@rm server client

编译方式:make

测试运行:

$ ./client
send data: [hello world] to 127.0.0.1:9999
$ ./server
recv len:11, data:[hello world] from 127.0.0.1:44180$ ./client "I can do it"
send data: [I can do it] to 127.0.0.1:9999
$ ./server
recv len:11, data:[I can do it] from 127.0.0.1:43607

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/918162.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

各种软件的官方文档和安装包下载地址记录

各种软件的官方文档和安装包下载地址记录组件 官网文档 官方下载地址Elasticsearch https://www.elastic.co/guide/en/elasticsearch/reference/index.html https://www.elastic.co/downloads/past-releases?product=…

基于导频的OFDM系统的信道估计(使用LS估计算法)

在OFDM系统中,信道估计是关键的一步,因为它直接影响到信号的解调和系统性能。最小二乘(LS)估计算法是一种常用的信道估计方法,它通过已知的导频符号来估计信道响应。 基于MATLAB的OFDM系统信道估计的实现,使用LS…

Day22super详解

super是一个关键字,用于子类访问父类的属性方法与构造器,与this一样在调用构造器的时候两者由于java语法的强制要求,都需要放在第一行,所以当用this取调用子类的构造器是,无法在用super去调用父类的构造器,否则会…

台州网站搜索排名巴中建设网站

千视携NDI 6技术闪耀2024 CCBN展会&#xff0c;呈现轻量化媒体解决方案 2024年4月24日至26日&#xff0c;北京首钢会展中心将举办第三十届中国国际广播电视网络技术展览会&#xff08;CCBN2024&#xff09;。这是中国广播电视行业的一项重要盛会&#xff0c;将有国内外超600家…

九讯鹿网站建设导购个人网站怎么做的

1、UML-4+1视图 UML-4+1视图将会与后面的架构4+1视图会一一对应上 视图往往出现在什么场景:我们看待一个事物,我们觉得它很复杂,难以搞清楚,为了化繁为简,我们会从一个侧面去看,这就是视图。而4+1视图就是分不同角度去看事物。 逻辑视图(logical view) 一般使用类与对…

外发图纸如何控制的最佳实践与注意事项

在工程项目中,外发图纸的管理非常重要。为了保证图纸的准确性和安全性,各个环节都需要有效控制。首先,标准化流程是基础,确保图纸的生成和审核符合要求。此外,使用像Ftrans B2B企业间文件安全交换系统这样的工具,…

Gitee:中国开发者生态的数字底座正在重构技术格局

Gitee:中国开发者生态的"数字底座"正在重构技术格局 在全球数字化转型加速推进的当下,中国开发者生态正在经历一场深刻的变革。作为本土领先的代码托管平台,Gitee凭借其独特的本土化优势和创新服务模式,…

快递100

[ { "label":"中通快递", "value":"zhongtong", "type":"国内运输商" }, { "label":"圆通速递&q…

文件同步软件是什么?主要有哪几种类型?

文件同步软件在现代企业中扮演着关键角色,帮助保障数据一致性和提高协作效率。它的运用不仅能确保团队成员在不同设备间实时共享文件,还能简化数据管理流程。以Ftrans FTC文件传输管控系统为例,这种软件支持多种文件…

“铸网2025”山东省工业和互联网CTF竞赛-web

还是太菜了源码 <?php highlight_file(__FILE__); class MGkk8 {public $a;public $b;public function rpl2(){$b = $this->b;if ($this->a == "RPG") {($b->a)($b->b."");}} } c…

辽宁模板网站建设公司晋州做网站的联系电话

深入miniqmt&#xff1a;创建交易对象的完整指南 在量化交易领域&#xff0c;miniqmt作为一个强大的工具&#xff0c;为开发者提供了执行程序化交易的接口。在进行任何交易操作之前&#xff0c;首先需要创建一个交易对象。本文将详细介绍如何在miniqmt中创建并配置交易对象&am…

python+springboot+uniapp微信小代码“美好食荐”框架 美食推荐 菜谱展示 用户互动 评论收藏框架

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

领嵌iLeadE-588网关AI边缘计算盒子一键部署二次开发

扩展接口丰富: 供电 12V 指示灯 5V电源指示灯 RS485 3路隔离RS485 RS232 1路调试串口 CAN 2路隔离CAN DI 4路 DO 4路 HDMI 1路 Audio 1路 AHD摄像头 4路 Ethernet 4路 4G/5G 1路 WiFi/BT 1路 USB3.0 OTG 1路 程序烧录…

2025年值得选的文件摆渡系统品牌解析

在数据安全与传输效率需求并行的当下,文件摆渡系统品牌已成为企业跨网协作的核心工具。传统传输方式常因安全漏洞与管理低效,导致数据泄露风险与运维成本攀升。而专业品牌如Ftrans Ferry跨网文件安全交换系统,通过自…

分布式专题——14 RabbitMQ之集群实战 - 指南

分布式专题——14 RabbitMQ之集群实战 - 指南2025-09-26 10:57 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: b…

QT与Spring Boot通信:实现HTTP请求的完整指南 - 教程

QT与Spring Boot通信:实现HTTP请求的完整指南 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&qu…

全球知名的Java Web开发平台Vaadin上线慧都网!

近日,慧都科技正式与国际知名的企业级Java Web开发平台供应商Vaadin达成合作伙伴关系。双方的合作,不仅补强了慧都在前端与应用开发领域的能力,更标志着慧都在软件工程数字化方案版图上的进一步扩展与深化,也为中国…

C#实现与欧姆龙PLC通信

C#实现与欧姆龙PLC通信,整合了FINS-TCP协议实现和主流开发框架:一、通信方案选型对比协议类型 适用场景 开发复杂度 推荐库FINS-TCP 欧姆龙设备专用通信 中等 HslCommunicationModbus TCP 多品牌设备通用通信 高 NMo…

linux docker 配置外网拉镜像

1. 先安装代理 https://github.com/nelvko/clash-for-linux-install?tab=readme-ov-file 2. sudo mkdir -p /etc/systemd/system/docker.service.d sudo nano /etc/systemd/system/docker.service.d/http-proxy.c…