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

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

一、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.hskmm.com/?act=detail&tid=17779

相关文章:

  • 各种软件的官方文档和安装包下载地址记录
  • 基于导频的OFDM系统的信道估计(使用LS估计算法)
  • Day22super详解
  • 外发图纸如何控制的最佳实践与注意事项
  • Gitee:中国开发者生态的数字底座正在重构技术格局
  • 快递100
  • 文件同步软件是什么?主要有哪几种类型?
  • “铸网2025”山东省工业和互联网CTF竞赛-web
  • 领嵌iLeadE-588网关AI边缘计算盒子一键部署二次开发
  • 2025年值得选的文件摆渡系统品牌解析
  • 全球知名的Java Web开发平台Vaadin上线慧都网!
  • C#实现与欧姆龙PLC通信
  • linux docker 配置外网拉镜像
  • 什么是跨网文件摆渡系统?IT运维效率提升300%的秘密武器
  • 借助Aspose.Email,在 Python中创建事件日历
  • 实用指南:【JavaEE初阶】多线程重点知识以及常考的面试题-多线程进阶(三)
  • C++ map 和unordered_map 的区别
  • 【英语启蒙动画合集】0基础宝宝必看的动画,超全!直接下载~
  • 基于OPC UA协议的SIMATIC PLC通信实现
  • AI 自动化智能体训练营 | 借助人工智能提升工作效率,打造自己的智能体工作流
  • MX-X21
  • Kubernetes Cilium网络组件和CoreDNS配置
  • 题解:P10107 [GDKOI2023 提高组] 树
  • Gitee Wiki:AI赋能的下一代研发知识管理平台如何重塑软件行业协作范式
  • COLMAP 安装在ubuntu20服务器上问题解决全记录
  • 完整教程:Prompt Tuning提示词微调工程
  • Autodesk Moldflow 2026下载地址与安装教程
  • 程序员利用Python分析股票赚钱,开发了股票行情看板
  • 9.26
  • K8S Deployment 学习