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

网络编程-TCP并发服务器-多点通信-域套接字

多进程实现TCP并发:依赖于while循环模式可初步实现并发功能,但是由于accpet函数和读写函数是阻塞的,导致必须要等待阻塞结束下一个用户才能连接,所以考虑使用多进程。

思路:将与客户端建立连接设置成父进程,将与客户端通信设置成子进程,考虑到客户结束通信需退出子进程,并防止僵尸进程,需回收子进程资源,故将回收进程函数设置成非阻塞,同时利用signal函数(SIGCHLD)实现一旦发现子进程死亡,立即发送信号回收进程资源,最后还需考虑子进程是完全拷贝父进程内存,需分别关闭文件描述符。

#include<head.h>
void handler(int signo);
int main(int argc, const char *argv[])
{if(signal(SIGCHLD,handler)==SIG_ERR){perror("signal");return -1;}//创建进程并发服务端//创建端点int sfd=socket(AF_INET,SOCK_STREAM,0);//参数1表示使用ipv4通信域//参数2表示使用TCP面向连接的通信方式//参数3表示补充通信协议,由于第二个参数已经指定通信方式,故写0if(sfd==-1){perror("socket");return 1;}else{printf("创建端点成功\n");}//调用端口快速重用函数int reuse=1;if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1){perror("setsockopt");return -1;}//将套结文件与IP地址与端口绑定struct sockaddr_in ip_port;char* ip="192.168.176.130";uint16_t port=8888;ip_port.sin_family=AF_INET;ip_port.sin_port=htons(port);ip_port.sin_addr.s_addr=inet_addr(ip);int retbind=bind(sfd,(struct sockaddr*)&ip_port,sizeof(ip_port));if(retbind==-1){perror("bind");return 1;}else if(retbind==0){printf("端口和IP绑定成功\n");}//将套接字设置为被动监听模式int retlisten=listen(sfd,128);if(retlisten==-1){perror("listen");return 1;}else if(retlisten==0){printf("设置监听成功\n");}//阻塞等待客户端连接struct sockaddr_in ser_ip_port;socklen_t addrlen=sizeof(ser_ip_port);int retaccept=-1;while(1){//父进程负责等待客户连接retaccept=accept(sfd,(struct sockaddr*)&ser_ip_port,&addrlen);if(retaccept==-1){perror("accept");return 1;}else{printf("客户端连接成功\n");printf("客户端IP:%s 客户端端口:%d\n",inet_ntoa(ser_ip_port.sin_addr),ntohs(ser_ip_port.sin_port));}pid_t pid=fork();if(pid>0){//父进程close(retaccept);}//与客户端相互通信else if(pid==0){close(sfd);//子进程完全拷贝父进程,所以需要关闭sfdchar wbuf[128]={0};char rbuf[128]={0};while(1){bzero(wbuf,0);bzero(rbuf,0);int retread=read(retaccept,rbuf,128);if(retread==0){//套结断开连接printf("客户端断开连接\n");break;}printf("读取客户端内容:%s\n",rbuf);strcat(rbuf,"<服务端已成功读取>");write(retaccept,rbuf,128);printf("成功回复客服端\n");}close(retaccept);//退出子进程exit(EXIT_SUCCESS);}else{perror("fork");return -1;}}close(sfd);return 0;
} 
void handler(int signo){if(signo==SIGCHLD){//设置成非阻塞while(waitpid(-1,NULL,WNOHANG)>0);}}

多线程实现TCP并发:

思路:主线程实现与客户端的连接,子线程实现与客户端实现通信,由于线程是去运行指定的代码片,所以避免不了局部变量问题,所以需注意指定运行的代码与主函数之间变量的关系,需要传参。

#include<head.h>
void* interaction(void* arg);
int main(int argc, const char *argv[])
{//使用线程实现服务端并发//创建端点int sfd=socket(AF_INET,SOCK_STREAM,0);//参数1表示使用ipv4通信域//参数2表示使用TCP面向连接的通信方式//参数3表示补充通信协议,由于第二个参数已经指定通信方式,故写0if(sfd==-1){perror("socket");return 1;}else{printf("创建端点成功\n");}//将套结文件与IP地址与端口绑定struct sockaddr_in ip_port;char* ip="192.168.176.130";uint16_t port=9999;ip_port.sin_family=AF_INET;ip_port.sin_port=htons(port);ip_port.sin_addr.s_addr=inet_addr(ip);int retbind=bind(sfd,(struct sockaddr*)&ip_port,sizeof(ip_port));if(retbind==-1){perror("bind");return 1;}else if(retbind==0){printf("端口和IP绑定成功\n");}//将套接字设置为被动监听模式int retlisten=listen(sfd,128);if(retlisten==-1){perror("listen");return 1;}else if(retlisten==0){printf("设置监听成功\n");}//阻塞等待客户端连接struct sockaddr_in ser_ip_port;socklen_t addrlen=sizeof(ser_ip_port);while(1){int retaccept=accept(sfd,(struct sockaddr*)&ser_ip_port,&addrlen);if(retaccept==-1){perror("accept");return 1;}else{printf("客户端连接成功\n");printf("客户端IP:%s 客户端端口:%d\n",inet_ntoa(ser_ip_port.sin_addr),ntohs(ser_ip_port.sin_port));}int inter_parameter=retaccept;pthread_t id;int retpthread_create=pthread_create(&id,0,interaction,&inter_parameter);if(retpthread_create!=0){perror("pthread_creat");return -1;}pthread_detach(id);}close(sfd);return 0; 
}
void* interaction(void* arg){//与客户端相互通信//解析argint retaccept=*(int*)arg;char wbuf[128]={0};char rbuf[128]={0};while(1){bzero(wbuf,0);bzero(rbuf,0);                                        int retread=read(retaccept,rbuf,128);if(retread==0){//套结断开连接printf("客户端断开连接\n");break;}printf("读取客户端内容:%s\n",rbuf);strcat(rbuf,"<服务端已成功读取>");write(retaccept,rbuf,128);printf("成功回复客服端\n");}close(retaccept);pthread_exit(NULL);
}

多点通信——广播发送端实现:

#include<head.h>
int main(int argc, const char *argv[])
{//发送端实现//创建套接文件int rtsocket=socket(AF_INET,SOCK_DGRAM,0);if(rtsocket==-1){perror("socket");return -1;}else{printf("creat success\n");}//设置属性允许发送广播数据int res=-1;int reslen=sizeof(res);int rtgetsockopt=getsockopt(rtsocket,SOL_SOCKET,SO_BROADCAST,&res,&reslen);if(rtgetsockopt==-1){perror("getsockopt");return -1;}else{printf("res=%d\n",res);}int reluse=1;int rtsetsockopt=setsockopt(rtsocket,SOL_SOCKET,SO_BROADCAST,&reluse,sizeof(reluse));if(rtsetsockopt==-1){perror("setsockopt");return -1;}getsockopt(rtsocket,SOL_SOCKET,SO_BROADCAST,&res,&reslen);if(rtgetsockopt==-1){perror("getsockopt");return -1;}else{printf("res=%d\n",res);}//绑定ipyu端口(可选)//向广播发送消息struct sockaddr_in rin;char* bip="192.168.176.255";uint16_t bport=6666;                 rin.sin_family=AF_INET;rin.sin_port=htons(bport);rin.sin_addr.s_addr=inet_addr(bip);char wbuf[128]={0};while(1){bzero(wbuf,128);fgets(wbuf,128,stdin);wbuf[strlen(wbuf)-1]=0;ssize_t rtsendto=sendto(rtsocket,wbuf,sizeof(wbuf),0,(struct sockaddr*)&rin,sizeof(rin));if(rtsendto==-1){perror("sendto");return -1;}if(strcmp(wbuf,"over")==0){break;}}close(rtsocket);return 0;
} 

多点通信——广播接收端实现:

#include<head.h>
int main(int argc, const char *argv[])
{//接收端实现//创建套接文件int rtsocket=socket(AF_INET,SOCK_DGRAM,0);if(rtsocket==-1){perror("socket");return -1;}//填充结构体信息struct sockaddr_in rin;char* bip="192.168.176.255";uint16_t bport=6666;rin.sin_family=AF_INET;rin.sin_port=htons(bport);rin.sin_addr.s_addr=inet_addr(bip);//绑定广播端口号和IP(必须绑定)int rtbind=bind(rtsocket,(struct sockaddr*)&rin,sizeof(rin));if(rtbind==-1){perror("bind");return -1;}//接收广播信息char rbuf[128]={0};while(1){memset(rbuf,0,128);ssize_t rtrecv=recv(rtsocket,rbuf,sizeof(rbuf),0);if(rtrecv==-1){perror("recv");return -1;}printf("广播消息%s\n:",rbuf);if(strcmp(rbuf,"over")==0){break;}}close(rtsocket);return 0;
} 

多点通信——组播接收端实现:

#include<head.h>
int main(int argc, const char *argv[])
{//使用组播通信方式实现接收端int rtsocket=socket(AF_INET,SOCK_DGRAM,0);if(rtsocket==-1){perror("socket");return -1;}//将套接字加入多播组struct ip_mreqn ip_m;ip_m.imr_multiaddr.s_addr=inet_addr("224.1.2.3");ip_m.imr_address.s_addr=inet_addr("192.168.176.130");ip_m.imr_ifindex=2;int rtsetsockopt=setsockopt(rtsocket,IPPROTO_IP,IP_ADD_MEMBERSHIP,&ip_m,sizeof(ip_m));if(rtsetsockopt==-1){perror("setsockopt");return -1;}//填充地址信息结构体 struct sockaddr_in rin;rin.sin_family=AF_INET;uint16_t rport=5555;rin.sin_port=htons(rport);rin.sin_addr.s_addr=inet_addr("224.1.2.3");//绑定(必须)int rtbind=bind(rtsocket,(struct sockaddr*)&rin,sizeof(rin));if(rtbind==-1){perror("bind");return -1;}//接收消息char rbuf[128]={0};while(1){bzero(rbuf,128);int rtrecv=recv(rtsocket,rbuf,sizeof(rbuf),0);if(rtrecv==-1){perror("recv");return -1;}printf("接收的消息:%s\n",rbuf);if(strcmp(rbuf,"over")==0){break;}}close(rtsocket);return 0;
}

多点通信——组播发送端实现:

#include<head.h>
int main(int argc, const char *argv[])
{//组播发送端int rtsocket=socket(AF_INET,SOCK_DGRAM,0);if(rtsocket==-1){perror("socket");return -1;}struct sockaddr_in sin;sin.sin_family=AF_INET;sin.sin_port=htons(5555);sin.sin_addr.s_addr=inet_addr("224.1.2.3");char wbuf[128]={0};while(1){memset(wbuf,0,128);fgets(wbuf,sizeof(wbuf),stdin);wbuf[strlen(wbuf)-1]=0;ssize_t rtsendto=sendto(rtsocket,wbuf,sizeof(wbuf),0,(struct sockaddr*)&sin,sizeof(sin));if(rtsendto==-1){perror("sendto");return -1;}printf("发送成功\n");if(strcmp(wbuf,"over")==0){break;}}close(rtsocket);return 0;
}
u

流式域套接字——服务端实现:

#include<head.h>
int main(int argc, const char *argv[])
{//使用域套接创建服务端//创建端点int sfd=socket(AF_UNIX,SOCK_STREAM,0);//参数1表示使用域套接通信域//参数2表示使用TCP面向连接的通信方式//参数3表示补充通信协议,由于第二个参数已经指定通信方式,故写0if(sfd==-1){perror("socket");return 1;}else{printf("创建端点成功\n");}//判断套接文件是否存在,因为存在再重新创建并绑定会报错if(access("./unix",F_OK)==0){unlink("./unix");//存在则删除文件}//删除后,将套结文件与通信域与套接文件路径绑定struct sockaddr_un un;un.sun_family=AF_UNIX;strcpy(un.sun_path,"./unix");int retbind=bind(sfd,(struct sockaddr*)&un,sizeof(un));if(retbind==-1){perror("bind");return 1;}else if(retbind==0){printf("端口和IP绑定成功\n");}//将套接字设置为被动监听模式int retlisten=listen(sfd,128);if(retlisten==-1){perror("listen");return 1;}else if(retlisten==0){printf("设置监听成功\n");}//阻塞等待客户端连接 struct sockaddr_un sun;socklen_t addrlen=sizeof(sun);int retaccept=accept(sfd,(struct sockaddr*)&sun,&addrlen);if(retaccept==-1){perror("accept");return 1;}else{printf("客户端连接成功\n");printf("套接文件:[%s]\n",sun.sun_path);}//与客户端相互通信char wbuf[128]={0};char rbuf[128]={0};while(1){bzero(wbuf,0);bzero(rbuf,0);int retread=read(retaccept,rbuf,128);if(retread==0){//套结断开连接printf("客户端断开连接\n");break;}printf("读取客户端内容:%s\n",rbuf);strcat(rbuf,"<服务端已成功读取>");write(retaccept,rbuf,128);printf("成功回复客服端\n");}close(retaccept);close(sfd);return 0;
} 

流式域套接字——客户端实现:

#include<head.h>
int main(int argc, const char *argv[])
{//使用域套接创建一个客户端//创建端点int sfd=socket(AF_UNIX,SOCK_STREAM,0);if(sfd==-1){perror("socket");return 1;}else{printf("创建成功\n");}//判断文件是否存在 if(access("./lunix",F_OK)==0){unlink("./lunix");//存在则删除文件}//将套结文件绑定struct sockaddr_un un;un.sun_family=AF_UNIX;strcpy(un.sun_path,"./lunix");int retbind=bind(sfd,(struct sockaddr*)&un,sizeof(un));if(retbind==-1){perror("bind");return 1;}else if(retbind==0){printf("IP与端口绑定成功\n");}//与服务器连接struct sockaddr_un run;run.sun_family=AF_UNIX;strcpy(run.sun_path,"./unix");//填充需要发送服务端的套接文件int retconnect=connect(sfd,(struct sockaddr*)&run,sizeof(run));if(retconnect==-1){perror("connect");return 1;}else if(retconnect==0){printf("连接成功\n");}//与服务器相互通信char rbuf[128]={0};char wbuf[128]={0};while(1){memset(rbuf,0,128);memset(wbuf,0,128); printf("请向服务端发送信息:");fgets(wbuf,128,stdin);wbuf[strlen(wbuf)-1]=0;send(sfd,wbuf,sizeof(wbuf),0);printf("向服务端发送消息成功\n");ssize_t retrecv=recv(sfd,rbuf,128,0);printf("接受服务端信息:%s\n",rbuf);}close(sfd);return 0;
}

报式域套接字——服务端实现:

#include<head.h>
int main(int argc, const char *argv[])
{//使用域套接创建服务端//创建一个端点int retsocket=socket(AF_UNIX,SOCK_DGRAM,0);if(retsocket==-1){perror("socket");return -1;}else{printf("创建端点成功\n");}//判断要绑定的套接文件是否存在,存在则删除(存在文件再重新绑定会报错)if(access("./udpunix",F_OK)==0){unlink("./udpunix");//存在则删除}//绑定套接字文件struct sockaddr_un un;un.sun_family=AF_UNIX;strcpy(un.sun_path,"./udpunix");int retbind=bind(retsocket,(struct sockaddr*)&un,sizeof(un));if(retbind==-1){perror("bind");return -1;}else if(retbind==0){printf("绑定成功\n");}//与客服端相互通信struct sockaddr_un sun;char rbuf[128]={0};char wbuf[128]={0};socklen_t addrlen=sizeof(sun);while(1){bzero(rbuf,0);bzero(wbuf,0);ssize_t retrecvfrom=recvfrom(retsocket,rbuf,128,0,(struct sockaddr*)&sun,&addrlen);if(retrecvfrom==-1){perror("recvfrom");return -1;}else{printf("读取客服端消息成功\n");printf("读取客服端消息:%s\n",rbuf);}//判断消息内容,只与一个人连接if(strcmp(rbuf,"connect")==0){connect(retsocket,(struct sockaddr*)&sun,sizeof(sun));}else if(strcmp(rbuf,"disconnect")==0){//断开连接sun.sun_family=AF_UNSPEC;connect(retsocket,(struct sockaddr*)&sun,sizeof(sun));}printf("请向客服端发送消息\n");fgets(wbuf,128,stdin);wbuf[strlen(wbuf)-1]=0;//清除空格ssize_t retsendto=sendto(retsocket,wbuf,strlen(wbuf),0,(struct sockaddr*)&sun,addrlen);if(retsendto==-1){perror("sendto");return -1;}else{printf("向客服端发送消息成功\n"); }}close(retsocket);return 0;
}

报式域套接字——客户端实现:

#include<head.h>
int main(int argc, const char *argv[])
{//创建客服端//创建一个端点,即套接字文件int retsocket=socket(AF_UNIX,SOCK_DGRAM,0);if(retsocket==-1){perror("socket");return -1;}else{printf("创建端点成功\n");}//判断要绑定的套接文件是否存在,存在则删除(存在文件再重新绑定会报错)if(access("./ludpunix",F_OK)==0){unlink("./ludpunix");//存在则删除}//使套接字绑定struct sockaddr_un un;un.sun_family=AF_UNIX;strcpy(un.sun_path,"./ludpunix");int retbind=bind(retsocket,(struct sockaddr*)&un,sizeof(un));if(retbind==-1){perror("bind");return -1;}else if(retbind==0){printf("绑定成功\n");}//与服务端相互通信struct sockaddr_un serun;serun.sun_family=AF_UNIX;strcpy(serun.sun_path,"./udpunix");char rbuf[128]={0};char wbuf[128]={0};socklen_t addrlen=sizeof(serun);while(1){bzero(rbuf,0);bzero(wbuf,0);printf("请向服务端发送消息\n");fgets(wbuf,128,stdin);wbuf[strlen(wbuf)-1]=0;//清除空格ssize_t retsendto=sendto(retsocket,wbuf,strlen(wbuf),0,(struct sockaddr*)&serun,addrlen);if(retsendto==-1){perror("sendto");return -1;}ssize_t retrecv=recvfrom(retsocket,rbuf,128,0,(struct sockaddr*)&serun,&addrlen);if(retrecv==-1){perror("sendto");return -1;}else{printf("读取服务端消息成功\n");printf("读取服务端消息:%s\n",rbuf);}}close(retsocket);return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 重学java 39.多线程 — 线程安全
  • 一篇文章讲透排序算法之希尔排序
  • 大摩:AI PC渗透率到2028年将达65%,联想和戴尔是最大受益者
  • 【linux】docker下nextcloud安装人脸识别插件
  • 碌时刻必备!微信自动回复让你告别消息堆积
  • 临时存储和永久存储的区别
  • HarmonyOS 鸿蒙应用开发 - 多态样式 stateStyles
  • solidworks画螺栓学习笔记
  • 自己手写一个栈【C风格】
  • Qml:线程
  • 93.网络游戏逆向分析与漏洞攻防-游戏技能系统分析-增强技能信息显示后进行分析
  • Redis批量删除指定前缀的key
  • STM32H743的FDCAN使用方法(2):STM32CubeMX初始化代码修改
  • 判断变量是否为数组的几种方法
  • 音视频及H264/H256编码相关原理
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • docker-consul
  • golang中接口赋值与方法集
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • PHP的Ev教程三(Periodic watcher)
  • 前端学习笔记之观察者模式
  • 通过几道题目学习二叉搜索树
  • 小而合理的前端理论:rscss和rsjs
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • ​如何防止网络攻击?
  • ​什么是bug?bug的源头在哪里?
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • (04)odoo视图操作
  • (k8s)Kubernetes本地存储接入
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (三)uboot源码分析
  • (算法)N皇后问题
  • (转)3D模板阴影原理
  • (转载)Linux网络编程入门
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • *p=a是把a的值赋给p,p=a是把a的地址赋给p。
  • .NET 6 在已知拓扑路径的情况下使用 Dijkstra,A*算法搜索最短路径
  • .net SqlSugarHelper
  • .Net Web项目创建比较不错的参考文章
  • .net8.0与halcon编程环境构建
  • .NET构架之我见
  • .NET企业级应用架构设计系列之应用服务器
  • .php结尾的域名,【php】php正则截取url中域名后的内容
  • [ 云计算 | AWS ] AI 编程助手新势力 Amazon CodeWhisperer:优势功能及实用技巧
  • [2018-01-08] Python强化周的第一天
  • [AIGC] Java 和 Kotlin 的区别
  • [android] 天气app布局练习
  • [BJDCTF2020]EzPHP1
  • [C# WPF] DataGrid选中行或选中单元格的背景和字体颜色修改
  • [C][栈帧]详细讲解
  • [c++] 单例模式 + cyberrt TimingWheel 单例分析
  • [HackMyVM]靶场 Quick3