REUSEADDR
一般当地址使用之后,就算服务器重启也不能马上使用这个ip地址。
因为此时网络处于TIME_WAIT状态,这个选项能在TIME_WAIT状态就使用ip地址
设置代码
int on = 1;if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)<0){ERR_EXIT("setsockopt");}
实现多客户连接
服务器
客户端不变
#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h>void do_service(int conn){char recvbuf[1024];while(1){memset(recvbuf, 0, sizeof(recvbuf));int ret = read(conn, recvbuf, sizeof(recvbuf));if(ret == 0){printf("client_close\n");break;} else if{ret == -1 }{ERR_EXIT("read");}fputs(recvbuf, stdout);write(conn, recvbuf, ret);}}close(conn);close(listenfd);}int main(){int listenfd;if(listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) < 0)//其实前两个已经表示为是tcp协议了,第三个参数可以填0{ERR_EXIT("socket");//报错误的宏}struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));//全部填充0servaddr.sin_family = AF_INET;servaddr.sin_prot = htons(5188);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//参数表示本机的任意地址,以下两种也是正确的写法//servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//inet_aton("127.0.0.1", &servaddr.sin_addr);if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){//一旦绑定完成,这个套接字就会被认为是被动套接字,否则是主动套接字。ERR_EXIT("bind");}if(listen(listenfd, SOMAXCONN) < 0)//后面的宏,代表队列的最大值{ERR_EXIT("listen");}struct sockaddr_in peeraddr;socklen_t peerlen = sizeof(peeraddr);int conn;//添加一个子进程完成多客户连接pid_t pid;while(1){if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0){//这里返回值是文件描述符,在没有客户端连接时accept会阻塞ERR_EXIT("accept");}pid = fork();//创建一个子进程if(pid == -1){ERR_EXIT("fork");}if(pid == 0){close(listenfd);do_service(conn);exit(EXIT_SUCCESS);//记得推出子进程。} else {close(conn);}}return 0;}
实现客户点对点的聊条
服务器
#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h>#include <signal.h>void handler(int sig){printf("recv a sig = %d\n", sig);exit(EXIT_SUCCESS);}int main(){int listenfd;if(listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) < 0)//其实前两个已经表示为是tcp协议了,第三个参数可以填0{ERR_EXIT("socket");//报错误的宏}struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));//全部填充0servaddr.sin_family = AF_INET;servaddr.sin_prot = htons(5188);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//参数表示本机的任意地址,以下两种也是正确的写法//servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//inet_aton("127.0.0.1", &servaddr.sin_addr);if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){//一旦绑定完成,这个套接字就会被认为是被动套接字,否则是主动套接字。ERR_EXIT("bind");}if(listen(listenfd, SOMAXCONN) < 0)//后面的宏,代表队列的最大值{ERR_EXIT("listen");}struct sockaddr_in peeraddr;socklen_t peerlen = sizeof(peeraddr);int conn;if((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0){//这里返回值是文件描述符ERR_EXIT("accept");}pid_t pid;//创建一个进程出来,一个进程用来接收数据,一个进程用来发送数据pid = fork();if(pid == -1){ERR_EXIT("fork");}if(pid == 0){signal(SIGUSR1, handler);//信号绑定一个函数,当子进程收到信号之后就会执行信号处理函数char sendbuf[1024] = {0};while(fgets(sendbuf, sizeof(sendbuf), stdin)!= NULL){write(conn, sendbuf, strlen(buf));memset(sendbuf, 0, sizeof(sendbuf));}printf("child close\n");exit(EXIT_SUCCESS);} else {char recvbuf[1024];while(1){memset(recvbuf, 0, sizeof(recvbuf));int ret = read(conn, recvbuf, sizeof(recvbuf));if(ret == -1){ERR_EXIT("read");} else if(ret == 0){printf("peer close\n");break;}fputs(recvbuf, stdout);}close(conn);close(listenfd);printf("father close\n");kill(pid, SIGUSR1)exit(EXIT_SUCCESS);}return 0;}
客户端
#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <stdlib.h>#include <stdio.h>#include <errno.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h>#include <signal.h>void handler(int sig){printf("recv a sig = %d\n", sig);exit(EXIT_SUCCESS);}int main(){int sock;if(sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) < 0)//其实前两个已经表示为是tcp协议了,第三个参数可以填0{ERR_EXIT("socket");//报错误的宏}struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));//全部填充0servaddr.sin_family = AF_INET;servaddr.sin_prot = htons(5188);servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");if(connect(sock, (struct sockaddr*)servaddr, sizeof(servaddr)) < 0){ERR_EXIT("connect");}pid_t pid;//创建一个进程出来,一个进程用来接收数据,一个进程用来发送数据pid = fork();if(pid == -1){ERR_EXIT("fork");}if(pid == 0){//子进程用来接收数据char recvbuf[1024];while(1){memset(recvbuf, 0, sizeof(recvbuf));int ret = read(sock, recvbuf, sizeof(recvbuf));if(ret == -1){ERR_EXIT("read");} else {printf("peer close\n");break;}fputs(recvbuf, stdout);}close(sock);kill(getppid(), SIGUSR1);//子进程退出的时候,让父进程退出} else { //父进程用来发送数据signal(SIGUSR1, handler);//绑定函数char sendbuf[1024] = {0};while(fgets(sendbuf, sizeof(sendbuf), stdin)!= NULL){write(sock, sendbuf, strlen(sendbuf));memset(sendbuf, 0, sizeof(sendbuf));//清空缓存区}close(sock);}return 0;}
信号实现进程间的通信
父进程通过信号类关闭子进程
使用 kill -l来查看所有信号
使用 SIGUSR1 用户自定义的信号
#include <signal.h>void handler(int sig){printf("recv a sig = %d, child close\n", sig);exit(EXIT_SUCCESS);}pid_t pid = fork();//在父进程中,fork的返回值等于子进程的pidif(pid == 0){signal(SIGUSR1, handler);//信号绑定一个函数,当子进程收到信号之后就会执行信号处理函数} else {//父进程退出时kill(pid, SIGUSR1)//向子进程发出一个信号}
子进程通过信号关闭父进程
#include <signal.h>void handler(int sig){printf("recv a sig = %d, child close\n", sig);exit(EXIT_SUCCESS);}pid_t pid = fork();if(pid == 0){kill(getppid(), SIGUSR1);//getppid()获取父进程pid号} else {signal(SIGUSR1, handler);//将信号与handler函数绑定}

