当前位置:   article > 正文

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

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

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

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

  1. #include<head.h>
  2. void handler(int signo);
  3. int main(int argc, const char *argv[])
  4. {
  5. if(signal(SIGCHLD,handler)==SIG_ERR){
  6. perror("signal");
  7. return -1;
  8. }
  9. //创建进程并发服务端
  10. //创建端点
  11. int sfd=socket(AF_INET,SOCK_STREAM,0);
  12. //参数1表示使用ipv4通信域
  13. //参数2表示使用TCP面向连接的通信方式
  14. //参数3表示补充通信协议,由于第二个参数已经指定通信方式,故写0
  15. if(sfd==-1){
  16. perror("socket");
  17. return 1;
  18. }
  19. else{
  20. printf("创建端点成功\n");
  21. }
  22. //调用端口快速重用函数
  23. int reuse=1;
  24. if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1){
  25. perror("setsockopt");
  26. return -1;
  27. }
  28. //将套结文件与IP地址与端口绑定
  29. struct sockaddr_in ip_port;
  30. char* ip="192.168.176.130";
  31. uint16_t port=8888;
  32. ip_port.sin_family=AF_INET;
  33. ip_port.sin_port=htons(port);
  34. ip_port.sin_addr.s_addr=inet_addr(ip);
  35. int retbind=bind(sfd,(struct sockaddr*)&ip_port,sizeof(ip_port));
  36. if(retbind==-1){
  37. perror("bind");
  38. return 1;
  39. }
  40. else if(retbind==0){
  41. printf("端口和IP绑定成功\n");
  42. }
  43. //将套接字设置为被动监听模式
  44. int retlisten=listen(sfd,128);
  45. if(retlisten==-1){
  46. perror("listen");
  47. return 1;
  48. }
  49. else if(retlisten==0){
  50. printf("设置监听成功\n");
  51. }
  52. //阻塞等待客户端连接
  53. struct sockaddr_in ser_ip_port;
  54. socklen_t addrlen=sizeof(ser_ip_port);
  55. int retaccept=-1;
  56. while(1){
  57. //父进程负责等待客户连接
  58. retaccept=accept(sfd,(struct sockaddr*)&ser_ip_port,&addrlen);
  59. if(retaccept==-1){
  60. perror("accept");
  61. return 1;
  62. }
  63. else{
  64. printf("客户端连接成功\n");
  65. printf("客户端IP:%s 客户端端口:%d\n",inet_ntoa(ser_ip_port.sin_addr),ntohs(ser_ip_port.sin_port));
  66. }
  67. pid_t pid=fork();
  68. if(pid>0){
  69. //父进程
  70. close(retaccept);
  71. }
  72. //与客户端相互通信
  73. else if(pid==0){
  74. close(sfd);//子进程完全拷贝父进程,所以需要关闭sfd
  75. char wbuf[128]={0};
  76. char rbuf[128]={0};
  77. while(1){
  78. bzero(wbuf,0);
  79. bzero(rbuf,0);
  80. int retread=read(retaccept,rbuf,128);
  81. if(retread==0){//套结断开连接
  82. printf("客户端断开连接\n");
  83. break;
  84. }
  85. printf("读取客户端内容:%s\n",rbuf);
  86. strcat(rbuf,"<服务端已成功读取>");
  87. write(retaccept,rbuf,128);
  88. printf("成功回复客服端\n");
  89. }
  90. close(retaccept);
  91. //退出子进程
  92. exit(EXIT_SUCCESS);
  93. }
  94. else{
  95. perror("fork");
  96. return -1;
  97. }
  98. }
  99. close(sfd);
  100. return 0;
  101. }
  102. void handler(int signo){
  103. if(signo==SIGCHLD){
  104. //设置成非阻塞
  105. while(waitpid(-1,NULL,WNOHANG)>0);
  106. }
  107. }

多线程实现TCP并发:

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

  1. #include<head.h>
  2. void* interaction(void* arg);
  3. int main(int argc, const char *argv[])
  4. {
  5. //使用线程实现服务端并发
  6. //创建端点
  7. int sfd=socket(AF_INET,SOCK_STREAM,0);
  8. //参数1表示使用ipv4通信域
  9. //参数2表示使用TCP面向连接的通信方式
  10. //参数3表示补充通信协议,由于第二个参数已经指定通信方式,故写0
  11. if(sfd==-1){
  12. perror("socket");
  13. return 1;
  14. }
  15. else{
  16. printf("创建端点成功\n");
  17. }
  18. //将套结文件与IP地址与端口绑定
  19. struct sockaddr_in ip_port;
  20. char* ip="192.168.176.130";
  21. uint16_t port=9999;
  22. ip_port.sin_family=AF_INET;
  23. ip_port.sin_port=htons(port);
  24. ip_port.sin_addr.s_addr=inet_addr(ip);
  25. int retbind=bind(sfd,(struct sockaddr*)&ip_port,sizeof(ip_port));
  26. if(retbind==-1){
  27. perror("bind");
  28. return 1;
  29. }
  30. else if(retbind==0){
  31. printf("端口和IP绑定成功\n");
  32. }
  33. //将套接字设置为被动监听模式
  34. int retlisten=listen(sfd,128);
  35. if(retlisten==-1){
  36. perror("listen");
  37. return 1;
  38. }
  39. else if(retlisten==0){
  40. printf("设置监听成功\n");
  41. }
  42. //阻塞等待客户端连接
  43. struct sockaddr_in ser_ip_port;
  44. socklen_t addrlen=sizeof(ser_ip_port);
  45. while(1){
  46. int retaccept=accept(sfd,(struct sockaddr*)&ser_ip_port,&addrlen);
  47. if(retaccept==-1){
  48. perror("accept");
  49. return 1;
  50. }
  51. else{
  52. printf("客户端连接成功\n");
  53. printf("客户端IP:%s 客户端端口:%d\n",inet_ntoa(ser_ip_port.sin_addr),ntohs(ser_ip_port.sin_port));
  54. }
  55. int inter_parameter=retaccept;
  56. pthread_t id;
  57. int retpthread_create=pthread_create(&id,0,interaction,&inter_parameter);
  58. if(retpthread_create!=0){
  59. perror("pthread_creat");
  60. return -1;
  61. }
  62. pthread_detach(id);
  63. }
  64. close(sfd);
  65. return 0;
  66. }
  67. void* interaction(void* arg){
  68. //与客户端相互通信
  69. //解析arg
  70. int retaccept=*(int*)arg;
  71. char wbuf[128]={0};
  72. char rbuf[128]={0};
  73. while(1){
  74. bzero(wbuf,0);
  75. bzero(rbuf,0);
  76. int retread=read(retaccept,rbuf,128);
  77. if(retread==0){//套结断开连接
  78. printf("客户端断开连接\n");
  79. break;
  80. }
  81. printf("读取客户端内容:%s\n",rbuf);
  82. strcat(rbuf,"<服务端已成功读取>");
  83. write(retaccept,rbuf,128);
  84. printf("成功回复客服端\n");
  85. }
  86. close(retaccept);
  87. pthread_exit(NULL);
  88. }

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

  1. #include<head.h>
  2. int main(int argc, const char *argv[])
  3. {
  4. //发送端实现
  5. //创建套接文件
  6. int rtsocket=socket(AF_INET,SOCK_DGRAM,0);
  7. if(rtsocket==-1){
  8. perror("socket");
  9. return -1;
  10. }
  11. else{
  12. printf("creat success\n");
  13. }
  14. //设置属性允许发送广播数据
  15. int res=-1;
  16. int reslen=sizeof(res);
  17. int rtgetsockopt=getsockopt(rtsocket,SOL_SOCKET,SO_BROADCAST,&res,&reslen);
  18. if(rtgetsockopt==-1){
  19. perror("getsockopt");
  20. return -1;
  21. }
  22. else{
  23. printf("res=%d\n",res);
  24. }
  25. int reluse=1;
  26. int rtsetsockopt=setsockopt(rtsocket,SOL_SOCKET,SO_BROADCAST,&reluse,sizeof(reluse));
  27. if(rtsetsockopt==-1){
  28. perror("setsockopt");
  29. return -1;
  30. }
  31. getsockopt(rtsocket,SOL_SOCKET,SO_BROADCAST,&res,&reslen);
  32. if(rtgetsockopt==-1){
  33. perror("getsockopt");
  34. return -1;
  35. }
  36. else{
  37. printf("res=%d\n",res);
  38. }
  39. //绑定ipyu端口(可选)
  40. //向广播发送消息
  41. struct sockaddr_in rin;
  42. char* bip="192.168.176.255";
  43. uint16_t bport=6666;
  44. rin.sin_family=AF_INET;
  45. rin.sin_port=htons(bport);
  46. rin.sin_addr.s_addr=inet_addr(bip);
  47. char wbuf[128]={0};
  48. while(1){
  49. bzero(wbuf,128);
  50. fgets(wbuf,128,stdin);
  51. wbuf[strlen(wbuf)-1]=0;
  52. ssize_t rtsendto=sendto(rtsocket,wbuf,sizeof(wbuf),0,(struct sockaddr*)&rin,sizeof(rin));
  53. if(rtsendto==-1){
  54. perror("sendto");
  55. return -1;
  56. }
  57. if(strcmp(wbuf,"over")==0){break;}
  58. }
  59. close(rtsocket);
  60. return 0;
  61. }

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

  1. #include<head.h>
  2. int main(int argc, const char *argv[])
  3. {
  4. //接收端实现
  5. //创建套接文件
  6. int rtsocket=socket(AF_INET,SOCK_DGRAM,0);
  7. if(rtsocket==-1){
  8. perror("socket");
  9. return -1;
  10. }
  11. //填充结构体信息
  12. struct sockaddr_in rin;
  13. char* bip="192.168.176.255";
  14. uint16_t bport=6666;
  15. rin.sin_family=AF_INET;
  16. rin.sin_port=htons(bport);
  17. rin.sin_addr.s_addr=inet_addr(bip);
  18. //绑定广播端口号和IP(必须绑定)
  19. int rtbind=bind(rtsocket,(struct sockaddr*)&rin,sizeof(rin));
  20. if(rtbind==-1){
  21. perror("bind");
  22. return -1;
  23. }
  24. //接收广播信息
  25. char rbuf[128]={0};
  26. while(1){
  27. memset(rbuf,0,128);
  28. ssize_t rtrecv=recv(rtsocket,rbuf,sizeof(rbuf),0);
  29. if(rtrecv==-1){
  30. perror("recv");
  31. return -1;
  32. }
  33. printf("广播消息%s\n:",rbuf);
  34. if(strcmp(rbuf,"over")==0){break;}
  35. }
  36. close(rtsocket);
  37. return 0;
  38. }

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

  1. #include<head.h>
  2. int main(int argc, const char *argv[])
  3. {
  4. //使用组播通信方式实现接收端
  5. int rtsocket=socket(AF_INET,SOCK_DGRAM,0);
  6. if(rtsocket==-1){
  7. perror("socket");
  8. return -1;
  9. }
  10. //将套接字加入多播组
  11. struct ip_mreqn ip_m;
  12. ip_m.imr_multiaddr.s_addr=inet_addr("224.1.2.3");
  13. ip_m.imr_address.s_addr=inet_addr("192.168.176.130");
  14. ip_m.imr_ifindex=2;
  15. int rtsetsockopt=setsockopt(rtsocket,IPPROTO_IP,IP_ADD_MEMBERSHIP,&ip_m,sizeof(ip_m));
  16. if(rtsetsockopt==-1){
  17. perror("setsockopt");
  18. return -1;
  19. }
  20. //填充地址信息结构体
  21. struct sockaddr_in rin;
  22. rin.sin_family=AF_INET;
  23. uint16_t rport=5555;
  24. rin.sin_port=htons(rport);
  25. rin.sin_addr.s_addr=inet_addr("224.1.2.3");
  26. //绑定(必须)
  27. int rtbind=bind(rtsocket,(struct sockaddr*)&rin,sizeof(rin));
  28. if(rtbind==-1){
  29. perror("bind");
  30. return -1;
  31. }
  32. //接收消息
  33. char rbuf[128]={0};
  34. while(1){
  35. bzero(rbuf,128);
  36. int rtrecv=recv(rtsocket,rbuf,sizeof(rbuf),0);
  37. if(rtrecv==-1){
  38. perror("recv");
  39. return -1;
  40. }
  41. printf("接收的消息:%s\n",rbuf);
  42. if(strcmp(rbuf,"over")==0){break;}
  43. }
  44. close(rtsocket);
  45. return 0;
  46. }

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

  1. #include<head.h>
  2. int main(int argc, const char *argv[])
  3. {
  4. //组播发送端
  5. int rtsocket=socket(AF_INET,SOCK_DGRAM,0);
  6. if(rtsocket==-1){
  7. perror("socket");
  8. return -1;
  9. }
  10. struct sockaddr_in sin;
  11. sin.sin_family=AF_INET;
  12. sin.sin_port=htons(5555);
  13. sin.sin_addr.s_addr=inet_addr("224.1.2.3");
  14. char wbuf[128]={0};
  15. while(1){
  16. memset(wbuf,0,128);
  17. fgets(wbuf,sizeof(wbuf),stdin);
  18. wbuf[strlen(wbuf)-1]=0;
  19. ssize_t rtsendto=sendto(rtsocket,wbuf,sizeof(wbuf),0,(struct sockaddr*)&sin,sizeof(sin));
  20. if(rtsendto==-1){
  21. perror("sendto");
  22. return -1;
  23. }
  24. printf("发送成功\n");
  25. if(strcmp(wbuf,"over")==0){break;}
  26. }
  27. close(rtsocket);
  28. return 0;
  29. }
  30. u

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

  1. #include<head.h>
  2. int main(int argc, const char *argv[])
  3. {
  4. //使用域套接创建服务端
  5. //创建端点
  6. int sfd=socket(AF_UNIX,SOCK_STREAM,0);
  7. //参数1表示使用域套接通信域
  8. //参数2表示使用TCP面向连接的通信方式
  9. //参数3表示补充通信协议,由于第二个参数已经指定通信方式,故写0
  10. if(sfd==-1){
  11. perror("socket");
  12. return 1;
  13. }
  14. else{
  15. printf("创建端点成功\n");
  16. }
  17. //判断套接文件是否存在,因为存在再重新创建并绑定会报错
  18. if(access("./unix",F_OK)==0){
  19. unlink("./unix");//存在则删除文件
  20. }
  21. //删除后,将套结文件与通信域与套接文件路径绑定
  22. struct sockaddr_un un;
  23. un.sun_family=AF_UNIX;
  24. strcpy(un.sun_path,"./unix");
  25. int retbind=bind(sfd,(struct sockaddr*)&un,sizeof(un));
  26. if(retbind==-1){
  27. perror("bind");
  28. return 1;
  29. }
  30. else if(retbind==0){
  31. printf("端口和IP绑定成功\n");
  32. }
  33. //将套接字设置为被动监听模式
  34. int retlisten=listen(sfd,128);
  35. if(retlisten==-1){
  36. perror("listen");
  37. return 1;
  38. }
  39. else if(retlisten==0){
  40. printf("设置监听成功\n");
  41. }
  42. //阻塞等待客户端连接
  43. struct sockaddr_un sun;
  44. socklen_t addrlen=sizeof(sun);
  45. int retaccept=accept(sfd,(struct sockaddr*)&sun,&addrlen);
  46. if(retaccept==-1){
  47. perror("accept");
  48. return 1;
  49. }
  50. else{
  51. printf("客户端连接成功\n");
  52. printf("套接文件:[%s]\n",sun.sun_path);
  53. }
  54. //与客户端相互通信
  55. char wbuf[128]={0};
  56. char rbuf[128]={0};
  57. while(1){
  58. bzero(wbuf,0);
  59. bzero(rbuf,0);
  60. int retread=read(retaccept,rbuf,128);
  61. if(retread==0){//套结断开连接
  62. printf("客户端断开连接\n");
  63. break;
  64. }
  65. printf("读取客户端内容:%s\n",rbuf);
  66. strcat(rbuf,"<服务端已成功读取>");
  67. write(retaccept,rbuf,128);
  68. printf("成功回复客服端\n");
  69. }
  70. close(retaccept);
  71. close(sfd);
  72. return 0;
  73. }

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

  1. #include<head.h>
  2. int main(int argc, const char *argv[])
  3. {
  4. //使用域套接创建一个客户端
  5. //创建端点
  6. int sfd=socket(AF_UNIX,SOCK_STREAM,0);
  7. if(sfd==-1){
  8. perror("socket");
  9. return 1;
  10. }
  11. else{
  12. printf("创建成功\n");
  13. }
  14. //判断文件是否存在
  15. if(access("./lunix",F_OK)==0){
  16. unlink("./lunix");//存在则删除文件
  17. }
  18. //将套结文件绑定
  19. struct sockaddr_un un;
  20. un.sun_family=AF_UNIX;
  21. strcpy(un.sun_path,"./lunix");
  22. int retbind=bind(sfd,(struct sockaddr*)&un,sizeof(un));
  23. if(retbind==-1){
  24. perror("bind");
  25. return 1;
  26. }
  27. else if(retbind==0){
  28. printf("IP与端口绑定成功\n");
  29. }
  30. //与服务器连接
  31. struct sockaddr_un run;
  32. run.sun_family=AF_UNIX;
  33. strcpy(run.sun_path,"./unix");//填充需要发送服务端的套接文件
  34. int retconnect=connect(sfd,(struct sockaddr*)&run,sizeof(run));
  35. if(retconnect==-1){
  36. perror("connect");
  37. return 1;
  38. }
  39. else if(retconnect==0){
  40. printf("连接成功\n");
  41. }
  42. //与服务器相互通信
  43. char rbuf[128]={0};
  44. char wbuf[128]={0};
  45. while(1){
  46. memset(rbuf,0,128);
  47. memset(wbuf,0,128);
  48. printf("请向服务端发送信息:");
  49. fgets(wbuf,128,stdin);
  50. wbuf[strlen(wbuf)-1]=0;
  51. send(sfd,wbuf,sizeof(wbuf),0);
  52. printf("向服务端发送消息成功\n");
  53. ssize_t retrecv=recv(sfd,rbuf,128,0);
  54. printf("接受服务端信息:%s\n",rbuf);
  55. }
  56. close(sfd);
  57. return 0;
  58. }

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

  1. #include<head.h>
  2. int main(int argc, const char *argv[])
  3. {
  4. //使用域套接创建服务端
  5. //创建一个端点
  6. int retsocket=socket(AF_UNIX,SOCK_DGRAM,0);
  7. if(retsocket==-1){
  8. perror("socket");
  9. return -1;
  10. }
  11. else{
  12. printf("创建端点成功\n");
  13. }
  14. //判断要绑定的套接文件是否存在,存在则删除(存在文件再重新绑定会报错)
  15. if(access("./udpunix",F_OK)==0){
  16. unlink("./udpunix");//存在则删除
  17. }
  18. //绑定套接字文件
  19. struct sockaddr_un un;
  20. un.sun_family=AF_UNIX;
  21. strcpy(un.sun_path,"./udpunix");
  22. int retbind=bind(retsocket,(struct sockaddr*)&un,sizeof(un));
  23. if(retbind==-1){
  24. perror("bind");
  25. return -1;
  26. }
  27. else if(retbind==0){
  28. printf("绑定成功\n");
  29. }
  30. //与客服端相互通信
  31. struct sockaddr_un sun;
  32. char rbuf[128]={0};
  33. char wbuf[128]={0};
  34. socklen_t addrlen=sizeof(sun);
  35. while(1){
  36. bzero(rbuf,0);
  37. bzero(wbuf,0);
  38. ssize_t retrecvfrom=recvfrom(retsocket,rbuf,128,0,(struct sockaddr*)&sun,&addrlen);
  39. if(retrecvfrom==-1){
  40. perror("recvfrom");
  41. return -1;
  42. }
  43. else{
  44. printf("读取客服端消息成功\n");
  45. printf("读取客服端消息:%s\n",rbuf);
  46. }
  47. //判断消息内容,只与一个人连接
  48. if(strcmp(rbuf,"connect")==0){
  49. connect(retsocket,(struct sockaddr*)&sun,sizeof(sun));
  50. }
  51. else if(strcmp(rbuf,"disconnect")==0){//断开连接
  52. sun.sun_family=AF_UNSPEC;
  53. connect(retsocket,(struct sockaddr*)&sun,sizeof(sun));
  54. }
  55. printf("请向客服端发送消息\n");
  56. fgets(wbuf,128,stdin);
  57. wbuf[strlen(wbuf)-1]=0;//清除空格
  58. ssize_t retsendto=sendto(retsocket,wbuf,strlen(wbuf),0,(struct sockaddr*)&sun,addrlen);
  59. if(retsendto==-1){
  60. perror("sendto");
  61. return -1;
  62. }
  63. else{
  64. printf("向客服端发送消息成功\n");
  65. }
  66. }
  67. close(retsocket);
  68. return 0;
  69. }

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

  1. #include<head.h>
  2. int main(int argc, const char *argv[])
  3. {
  4. //创建客服端
  5. //创建一个端点,即套接字文件
  6. int retsocket=socket(AF_UNIX,SOCK_DGRAM,0);
  7. if(retsocket==-1){
  8. perror("socket");
  9. return -1;
  10. }
  11. else{
  12. printf("创建端点成功\n");
  13. }
  14. //判断要绑定的套接文件是否存在,存在则删除(存在文件再重新绑定会报错)
  15. if(access("./ludpunix",F_OK)==0){
  16. unlink("./ludpunix");//存在则删除
  17. }
  18. //使套接字绑定
  19. struct sockaddr_un un;
  20. un.sun_family=AF_UNIX;
  21. strcpy(un.sun_path,"./ludpunix");
  22. int retbind=bind(retsocket,(struct sockaddr*)&un,sizeof(un));
  23. if(retbind==-1){
  24. perror("bind");
  25. return -1;
  26. }
  27. else if(retbind==0){
  28. printf("绑定成功\n");
  29. }
  30. //与服务端相互通信
  31. struct sockaddr_un serun;
  32. serun.sun_family=AF_UNIX;
  33. strcpy(serun.sun_path,"./udpunix");
  34. char rbuf[128]={0};
  35. char wbuf[128]={0};
  36. socklen_t addrlen=sizeof(serun);
  37. while(1){
  38. bzero(rbuf,0);
  39. bzero(wbuf,0);
  40. printf("请向服务端发送消息\n");
  41. fgets(wbuf,128,stdin);
  42. wbuf[strlen(wbuf)-1]=0;//清除空格
  43. ssize_t retsendto=sendto(retsocket,wbuf,strlen(wbuf),0,(struct sockaddr*)&serun,addrlen);
  44. if(retsendto==-1){
  45. perror("sendto");
  46. return -1;
  47. }
  48. ssize_t retrecv=recvfrom(retsocket,rbuf,128,0,(struct sockaddr*)&serun,&addrlen);
  49. if(retrecv==-1){
  50. perror("sendto");
  51. return -1;
  52. }
  53. else{
  54. printf("读取服务端消息成功\n");
  55. printf("读取服务端消息:%s\n",rbuf);
  56. }
  57. }
  58. close(retsocket);
  59. return 0;
  60. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/639203
推荐阅读
相关标签
  

闽ICP备14008679号