REUSEADDR

一般当地址使用之后,就算服务器重启也不能马上使用这个ip地址。
因为此时网络处于TIME_WAIT状态,这个选项能在TIME_WAIT状态就使用ip地址
image.png

设置代码

  1. int on = 1;
  2. if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)<0){
  3. ERR_EXIT("setsockopt");
  4. }

实现多客户连接

image.png
image.png

服务器

客户端不变

  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <errno.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #include <string.h>
  10. void do_service(int conn){
  11. char recvbuf[1024];
  12. while(1){
  13. memset(recvbuf, 0, sizeof(recvbuf));
  14. int ret = read(conn, recvbuf, sizeof(recvbuf));
  15. if(ret == 0){
  16. printf("client_close\n");
  17. break;
  18. } else if{ret == -1 }{
  19. ERR_EXIT("read");
  20. }
  21. fputs(recvbuf, stdout);
  22. write(conn, recvbuf, ret);
  23. }
  24. }
  25. close(conn);
  26. close(listenfd);
  27. }
  28. int main(){
  29. int listenfd;
  30. if(listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) < 0)//其实前两个已经表示为是tcp协议了,第三个参数可以填0
  31. {
  32. ERR_EXIT("socket");//报错误的宏
  33. }
  34. struct sockaddr_in servaddr;
  35. memset(&servaddr, 0, sizeof(servaddr));//全部填充0
  36. servaddr.sin_family = AF_INET;
  37. servaddr.sin_prot = htons(5188);
  38. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//参数表示本机的任意地址,以下两种也是正确的写法
  39. //servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  40. //inet_aton("127.0.0.1", &servaddr.sin_addr);
  41. if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){//一旦绑定完成,这个套接字就会被认为是被动套接字,否则是主动套接字。
  42. ERR_EXIT("bind");
  43. }
  44. if(listen(listenfd, SOMAXCONN) < 0)//后面的宏,代表队列的最大值
  45. {
  46. ERR_EXIT("listen");
  47. }
  48. struct sockaddr_in peeraddr;
  49. socklen_t peerlen = sizeof(peeraddr);
  50. int conn;
  51. //添加一个子进程完成多客户连接
  52. pid_t pid;
  53. while(1){
  54. if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0){//这里返回值是文件描述符,在没有客户端连接时accept会阻塞
  55. ERR_EXIT("accept");
  56. }
  57. pid = fork();//创建一个子进程
  58. if(pid == -1){
  59. ERR_EXIT("fork");
  60. }
  61. if(pid == 0){
  62. close(listenfd);
  63. do_service(conn);
  64. exit(EXIT_SUCCESS);//记得推出子进程。
  65. } else {
  66. close(conn);
  67. }
  68. }
  69. return 0;
  70. }

实现客户点对点的聊条

服务器

  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <errno.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #include <string.h>
  10. #include <signal.h>
  11. void handler(int sig){
  12. printf("recv a sig = %d\n", sig);
  13. exit(EXIT_SUCCESS);
  14. }
  15. int main(){
  16. int listenfd;
  17. if(listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) < 0)//其实前两个已经表示为是tcp协议了,第三个参数可以填0
  18. {
  19. ERR_EXIT("socket");//报错误的宏
  20. }
  21. struct sockaddr_in servaddr;
  22. memset(&servaddr, 0, sizeof(servaddr));//全部填充0
  23. servaddr.sin_family = AF_INET;
  24. servaddr.sin_prot = htons(5188);
  25. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//参数表示本机的任意地址,以下两种也是正确的写法
  26. //servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  27. //inet_aton("127.0.0.1", &servaddr.sin_addr);
  28. if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){//一旦绑定完成,这个套接字就会被认为是被动套接字,否则是主动套接字。
  29. ERR_EXIT("bind");
  30. }
  31. if(listen(listenfd, SOMAXCONN) < 0)//后面的宏,代表队列的最大值
  32. {
  33. ERR_EXIT("listen");
  34. }
  35. struct sockaddr_in peeraddr;
  36. socklen_t peerlen = sizeof(peeraddr);
  37. int conn;
  38. if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0){//这里返回值是文件描述符
  39. ERR_EXIT("accept");
  40. }
  41. pid_t pid;//创建一个进程出来,一个进程用来接收数据,一个进程用来发送数据
  42. pid = fork();
  43. if(pid == -1){
  44. ERR_EXIT("fork");
  45. }
  46. if(pid == 0){
  47. signal(SIGUSR1, handler);//信号绑定一个函数,当子进程收到信号之后就会执行信号处理函数
  48. char sendbuf[1024] = {0};
  49. while(fgets(sendbuf, sizeof(sendbuf), stdin)!= NULL){
  50. write(conn, sendbuf, strlen(buf));
  51. memset(sendbuf, 0, sizeof(sendbuf));
  52. }
  53. printf("child close\n");
  54. exit(EXIT_SUCCESS);
  55. } else {
  56. char recvbuf[1024];
  57. while(1){
  58. memset(recvbuf, 0, sizeof(recvbuf));
  59. int ret = read(conn, recvbuf, sizeof(recvbuf));
  60. if(ret == -1){
  61. ERR_EXIT("read");
  62. } else if(ret == 0){
  63. printf("peer close\n");
  64. break;
  65. }
  66. fputs(recvbuf, stdout);
  67. }
  68. close(conn);
  69. close(listenfd);
  70. printf("father close\n");
  71. kill(pid, SIGUSR1)
  72. exit(EXIT_SUCCESS);
  73. }
  74. return 0;
  75. }

客户端

  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <errno.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #include <string.h>
  10. #include <signal.h>
  11. void handler(int sig){
  12. printf("recv a sig = %d\n", sig);
  13. exit(EXIT_SUCCESS);
  14. }
  15. int main(){
  16. int sock;
  17. if(sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) < 0)//其实前两个已经表示为是tcp协议了,第三个参数可以填0
  18. {
  19. ERR_EXIT("socket");//报错误的宏
  20. }
  21. struct sockaddr_in servaddr;
  22. memset(&servaddr, 0, sizeof(servaddr));//全部填充0
  23. servaddr.sin_family = AF_INET;
  24. servaddr.sin_prot = htons(5188);
  25. servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
  26. if(connect(sock, (struct sockaddr*)servaddr, sizeof(servaddr)) < 0){
  27. ERR_EXIT("connect");
  28. }
  29. pid_t pid;//创建一个进程出来,一个进程用来接收数据,一个进程用来发送数据
  30. pid = fork();
  31. if(pid == -1){
  32. ERR_EXIT("fork");
  33. }
  34. if(pid == 0){//子进程用来接收数据
  35. char recvbuf[1024];
  36. while(1){
  37. memset(recvbuf, 0, sizeof(recvbuf));
  38. int ret = read(sock, recvbuf, sizeof(recvbuf));
  39. if(ret == -1){
  40. ERR_EXIT("read");
  41. } else {
  42. printf("peer close\n");
  43. break;
  44. }
  45. fputs(recvbuf, stdout);
  46. }
  47. close(sock);
  48. kill(getppid(), SIGUSR1);//子进程退出的时候,让父进程退出
  49. } else { //父进程用来发送数据
  50. signal(SIGUSR1, handler);//绑定函数
  51. char sendbuf[1024] = {0};
  52. while(fgets(sendbuf, sizeof(sendbuf), stdin)!= NULL){
  53. write(sock, sendbuf, strlen(sendbuf));
  54. memset(sendbuf, 0, sizeof(sendbuf));//清空缓存区
  55. }
  56. close(sock);
  57. }
  58. return 0;
  59. }

信号实现进程间的通信

父进程通过信号类关闭子进程

使用 kill -l来查看所有信号
使用 SIGUSR1 用户自定义的信号

  1. #include <signal.h>
  2. void handler(int sig){
  3. printf("recv a sig = %d, child close\n", sig);
  4. exit(EXIT_SUCCESS);
  5. }
  6. pid_t pid = fork();//在父进程中,fork的返回值等于子进程的pid
  7. if(pid == 0){
  8. signal(SIGUSR1, handler);//信号绑定一个函数,当子进程收到信号之后就会执行信号处理函数
  9. } else {
  10. //父进程退出时
  11. kill(pid, SIGUSR1)//向子进程发出一个信号
  12. }

子进程通过信号关闭父进程

  1. #include <signal.h>
  2. void handler(int sig){
  3. printf("recv a sig = %d, child close\n", sig);
  4. exit(EXIT_SUCCESS);
  5. }
  6. pid_t pid = fork();
  7. if(pid == 0){
  8. kill(getppid(), SIGUSR1);//getppid()获取父进程pid号
  9. } else {
  10. signal(SIGUSR1, handler);//将信号与handler函数绑定
  11. }