有个SO_REUSEADDR值得注意一下:

服务器端尽可能使用SO_REUSEADDR
在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。
使用SO_REUSEADDR选项可以使得不必等待TIME_WAIT状态消失就可以重启服务器。
也就是如果你不这样子用的话会出现这样的问题:_
就是用上节的服务器和客户端通信的时候,如果不设置这个选项的话,当服务器端先退出的话,在启动服务器端的话会失败。可以用netstat -an | grep TIME_WAIT来查看一下。
具体设置:
看截图一目了然,具体怎么用man一下就可以,养成好习惯。
 
 
问题来了:上节的程序服务器端只可以接受一个客户端的连接。原因就在于当服务器端接收到一个客户端时它会进入到while中,那它就不能再accept了。
这时我们可以用父子进程配合的方式来实现,一个服务器端可以接受多个客户端的连接。
 
服务器端:
  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <netinet/in.h>
  5. #include <arpa/inet.h>
  6.  
  7. #include <stdlib.h>
  8. #include <stdio.h>
  9. #include <errno.h>
  10. #include <string.h>
  11.  
  12. #define ERR_EXIT(m) \
  13. do \
  14. { \
  15. perror(m); \
  16. exit(EXIT_FAILURE); \
  17. } while(0)
  18.  
  19. void do_service(int conn)
  20. {
  21. char recvbuf[1024];
  22. while (1)
  23. {
  24. memset(recvbuf, 0, sizeof(recvbuf));
  25. int ret = read(conn, recvbuf, sizeof(recvbuf));
  26. if (ret == 0) //服务器端要能捕捉到客户端的退出
  27. {
  28. printf("client close\n");
  29. break;
  30. }
  31. else if (ret == -1)
  32. ERR_EXIT("read");
  33. fputs(recvbuf, stdout);
  34. write(conn, recvbuf, ret);
  35. }
  36. }
  37.  
  38. int main(void)
  39. {
  40. int listenfd;
  41. if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
  42. /* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
  43. ERR_EXIT("socket");
  44.  
  45. struct sockaddr_in servaddr;
  46. memset(&servaddr, 0, sizeof(servaddr));
  47. servaddr.sin_family = AF_INET;
  48. servaddr.sin_port = htons(5188);
  49. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  50. /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
  51. /*inet_aton("127.0.0.1", &servaddr.sin_addr);*/
  52.      //这个就是上边说的那个服务器重启的解决方法
  53. int on = 1;
  54. if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
  55. ERR_EXIT("setsockopt");
  56.  
  57. if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
  58. ERR_EXIT("bind");
  59. if (listen(listenfd, SOMAXCONN) < 0)
  60. ERR_EXIT("listen");
  61.  
  62. struct sockaddr_in peeraddr;
  63. socklen_t peerlen = sizeof(peeraddr);
  64. int conn;
  65.  
  66. pid_t pid;
  67. while (1)
  68. {
  69. if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
  70. ERR_EXIT("accept");
  71.  
  72. printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
  73.  
  74. pid = fork();
  75. if (pid == -1)
  76. ERR_EXIT("fork");
  77. if (pid == 0)
  78. {
  79. close(listenfd);//子进程只负责接收后的工作
  80. do_service(conn); //这里写了一个自定义的函数来解决子进程的操作
  81. exit(EXIT_SUCCESS);
  82. }
  83. else
  84. close(conn); //父进程只负责监听接受连接,不负责具体的操作。
  85. }
  86.  
  87. return 0;
  88. }

其中还有很多的细节需要注意,大家亲自试一下,首先自己动手去写,自己试各种情况有什么问题,然后再回过头来看程序和自己写的差别,才会对细节的处理理解更深刻。

下面实现一个点对点的聊天程序:

服务器端:

  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <netinet/in.h>
  5. #include <arpa/inet.h>
  6. #include <signal.h>
  7.  
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include <string.h>
  12.  
  13. #define ERR_EXIT(m) \
  14. do \
  15. { \
  16. perror(m); \
  17. exit(EXIT_FAILURE); \
  18. } while(0)
  19.  
  20. void handler(int sig)
  21. {
  22. printf("recv a sig=%d\n", sig);
  23. exit(EXIT_SUCCESS);
  24. }
  25.  
  26. int main(void)
  27. {
  28. int listenfd;
  29. if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
  30. /* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
  31. ERR_EXIT("socket");
  32.  
  33. struct sockaddr_in servaddr;
  34. memset(&servaddr, 0, sizeof(servaddr));
  35. servaddr.sin_family = AF_INET;
  36. servaddr.sin_port = htons(5188);
  37. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  38. /*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
  39. /*inet_aton("127.0.0.1", &servaddr.sin_addr);*/
  40.  
  41. int on = 1;
  42. if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
  43. ERR_EXIT("setsockopt");
  44.  
  45. if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
  46. ERR_EXIT("bind");
  47. if (listen(listenfd, SOMAXCONN) < 0)
  48. ERR_EXIT("listen");
  49.  
  50. struct sockaddr_in peeraddr;
  51. socklen_t peerlen = sizeof(peeraddr);
  52. int conn;
  53. if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
  54. ERR_EXIT("accept");
  55.  
  56. printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
  57.  
  58. pid_t pid;
  59. pid = fork();
  60. if (pid == -1)
  61. ERR_EXIT("fork");
  62.  
  63. if (pid == 0)
  64. {
  65. signal(SIGUSR1, handler); //这里用到了信号处理,在父进程退出的时候会发一个信号过来给子进程,然后子进程也跟着退出
  66. char sendbuf[1024] = {0};
  67. while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
  68. {
  69. write(conn, sendbuf, strlen(sendbuf));
  70. memset(sendbuf, 0, sizeof(sendbuf));
  71. }
  72. printf("child close\n");
  73. exit(EXIT_SUCCESS);
  74. }
  75. else
  76. {
  77. char recvbuf[1024];
  78. while (1)
  79. {
  80. memset(recvbuf, 0, sizeof(recvbuf));
  81. int ret = read(conn, recvbuf, sizeof(recvbuf));
  82. if (ret == -1)
  83. ERR_EXIT("read");
  84. else if (ret == 0)
  85. {
  86. printf("peer close\n");
  87. break;
  88. }
  89.  
  90. fputs(recvbuf, stdout);
  91. }
  92. printf("parent close\n");
  93. kill(pid, SIGUSR1);//这是父进程退出前要向子进程发一个自定义的信号
  94. exit(EXIT_SUCCESS);
  95. }
  96.  
  97. return 0;
  98. }

客户端:

  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <netinet/in.h>
  5. #include <arpa/inet.h>
  6. #include <signal.h>
  7.  
  8. #include <stdlib.h>
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include <string.h>
  12.  
  13. #define ERR_EXIT(m) \
  14. do \
  15. { \
  16. perror(m); \
  17. exit(EXIT_FAILURE); \
  18. } while(0)
  19.  
  20. void handler(int sig)
  21. {
  22. printf("recv a sig=%d\n", sig);
  23. exit(EXIT_SUCCESS);
  24. }
  25.  
  26. int main(void)
  27. {
  28. int sock;
  29. if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
  30. ERR_EXIT("socket");
  31.  
  32. struct sockaddr_in servaddr;
  33. memset(&servaddr, 0, sizeof(servaddr));
  34. servaddr.sin_family = AF_INET;
  35. servaddr.sin_port = htons(5188);
  36. servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  37.  
  38. if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
  39. ERR_EXIT("connect");
  40.  
  41. pid_t pid;
  42. pid = fork();
  43. if (pid == -1)
  44. ERR_EXIT("fork");
  45.  
  46. if (pid == 0)
  47. {
  48. char recvbuf[1024];
  49. while (1)
  50. {
  51. memset(recvbuf, 0, sizeof(recvbuf));
  52. int ret = read(sock, recvbuf, sizeof(recvbuf));
  53. if (ret == -1)
  54. ERR_EXIT("read");
  55. else if (ret == 0)
  56. {
  57. printf("peer close\n");
  58. break;
  59. }
  60.  
  61. fputs(recvbuf, stdout);
  62. }
  63. close(sock);
  64. kill(getppid(), SIGUSR1);
  65. }
  66. else
  67. {
  68. signal(SIGUSR1, handler);
  69. char sendbuf[1024] = {0};
  70. while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
  71. {
  72. write(sock, sendbuf, strlen(sendbuf));
  73. memset(sendbuf, 0, sizeof(sendbuf));
  74. }
  75. close(sock);
  76. }
  77.  
  78. return 0;
  79. }

这一节信息量比较大,得好好消化一下。

08socket编程的更多相关文章

  1. python学习笔记08-- socket编程

    本节内容: 一.网络基础知识 二.socket概念及相关语法 2.1socket概念 2.2socket解释 2.3socket模块功能介绍 2.4socket粘包问题 2.5Socket多并发 一. ...

  2. 从直播编程到直播教育:LiveEdu.tv开启多元化的在线学习直播时代

    2015年9月,一个叫Livecoding.tv的网站在互联网上引起了编程界的注意.缘于Pingwest品玩的一位编辑在上网时无意中发现了这个网站,并写了一篇文章<一个比直播睡觉更奇怪的网站:直 ...

  3. JavaScript之父Brendan Eich,Clojure 创建者Rich Hickey,Python创建者Van Rossum等编程大牛对程序员的职业建议

    软件开发是现时很火的职业.据美国劳动局发布的一项统计数据显示,从2014年至2024年,美国就业市场对开发人员的需求量将增长17%,而这个增长率比起所有职业的平均需求量高出了7%.很多人年轻人会选择编 ...

  4. 读书笔记:JavaScript DOM 编程艺术(第二版)

    读完还是能学到很多的基础知识,这里记录下,方便回顾与及时查阅. 内容也有自己的一些补充. JavaScript DOM 编程艺术(第二版) 1.JavaScript简史 JavaScript由Nets ...

  5. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  6. C#异步编程(一)

    异步编程简介 前言 本人学习.Net两年有余,是第一次写博客,虽然写的很认真,当毕竟是第一次,肯定会有很多不足之处, 希望大家照顾照顾新人,有错误之处可以指出来,我会虚心接受的. 何谓异步 与同步相对 ...

  7. UE4新手之编程指南

    虚幻引擎4为程序员提供了两套工具集,可共同使用来加速开发的工作流程. 新的游戏类.Slate和Canvas用户接口元素以及编辑器功能可以使用C++语言来编写,并且在使用Visual Studio 或 ...

  8. C#与C++的发展历程第三 - C#5.0异步编程巅峰

    系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...

  9. 猫哥网络编程系列:HTTP PEM 万能调试法

    注:本文内容较长且细节较多,建议先收藏再阅读,原文将在 Github 上维护与更新. 在 HTTP 接口开发与调试过程中,我们经常遇到以下类似的问题: 为什么本地环境接口可以调用成功,但放到手机上就跑 ...

随机推荐

  1. 论文学习 - 《Hadoop平台下的海量数据存储技术研究》

    摘要 研究背景: 1. 互联网的图片数据急剧膨胀 2. Hadoop平台下的Hdfs分布式文件系统能够很好的处理海量数据 研究内容: 1. Hadoop平台工作原理 2. Hadoop平台下图片存储系 ...

  2. iOS7中如何去除UINavigationbar下边的那条黑线

    做项目过程中遇到要去掉导航栏下面的一条黑线,从网上找到的一个方法 默认UINavigationbar样式 准备用于替换的背景 替换后的效果 if ([self.navigationController ...

  3. C++ Primer : 第十二章 : 文本查询程序

    C++ Primer书上这个例子讲的很不错,写写帮助自己理解标准库和智能指针. .h 文件内容 #include <fstream> #include <iostream> # ...

  4. POJ 3461 裸的KMP

    直接贴代码吧 #include<cstdio> #include<cstring> ],T[]; ]; int n,m; void getfail() { f[] = ; f[ ...

  5. Android 客户端和服务器 json交互

    http://www.cnblogs.com/jyan/articles/2544974.html 1.JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式. ...

  6. Java中将16进制字符串转换成汉字

    技术交流群:233513714 /** * 将16进制字符串转换成汉字 * @param str * @return */ public static String deUnicode(String ...

  7. 黑马程序员——JAVA基础之程序控制流结构之循环结构,循环嵌套

    ------- android培训.java培训.期待与您交流! ---------- 循环结构: 代表语句:while ,do while ,for while语句格式 : while(条件表达式) ...

  8. caffe:编译时提示:unsupported GNU version! gcc versions later than 4.9 are not supported!

    NVCC src/caffe/solvers/adam_solver.cuIn file included from /usr/local/cuda/include/cuda_runtime.h:76 ...

  9. C++面向对象要点

    先说说面向对象思想的一个总体认识 对象通常会有行为,这些行为是靠信息支撑,这些信息包括外部信息和内部信息,对象行为会维护其中的一部分信息 因此对象可以看成是这样一种实体,它获取信息,然后决定自己的行为 ...

  10. KMP字符串模式匹配学习笔记

    KMP算法实验 1.编程计算模式串(子串)的next值.2.利用KMP算法在主串中找到模式串的位置. 参考代码:---------int getNexlVal( char * s,  int j)// ...