UNPv13:#第5章#TCP客戶/伺服器程式示例
阿新 • • 發佈:2022-05-03
Code
//server.c #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> #include <wait.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> void sig_chld(int signo) { pid_t pid; int stat; while((pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminatedn", pid); return; } int main(int argc, char* argv[]) { int listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(listenfd == -1) { perror("function socket error :"); exit(-1); } struct sockaddr_in serveraddr; memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); serveraddr.sin_port = htons(atoi(argv[1])); if(bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1) { perror("function bind error : "); exit(-1); } if(listen(listenfd, 5) == -1) { perror("function listen error : "); exit(-1); } struct sigaction act, oact; act.sa_handler = sig_chld; sigemptyset(&act.sa_mask); act.sa_flags = 0; if(sigaction(SIGCHLD, &act, &oact) == -1) { perror("function sigaction error : "); exit(-1); } int connfd; struct sockaddr_in clientaddr; socklen_t clientlen; while(1) { clientlen = sizeof(clientaddr); connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen); if(connfd == -1) { if(errno == EINTR) continue; perror("function accept error : "); exit(-1); } if(fork() == 0) { if(close(listenfd) == -1) { perror("function close error : "); exit(-1); } char buf[1024]; int len; AGAIN: while((len = read(connfd, buf, 1024)) > 0) write(connfd, buf, len); if(len < 0 && errno == EINTR) goto AGAIN; else if(len < 0) { perror("function read error : "); exit(1); } } else { if(close(connfd) == -1) { perror("function close error : "); exit(-1); } } } return 0; }
//client.c #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main(int argc, char* argv[]) { int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sockfd == -1) { perror("function socket error : "); exit(-1); } struct sockaddr_in serveraddr; memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; switch(inet_pton(AF_INET, argv[1], &serveraddr.sin_addr)) { case 0: perror("function inet_pton error (input invalid) : "); exit(-1); break; case 1: //successed break; default: perror("function inet_pton error : "); exit(-1); break; } serveraddr.sin_port = htons(atoi(argv[2])); connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)); char buf[1024]; while(fgets(buf, 1024, stdin) != NULL) { write(sockfd, buf, strlen(buf)); if(read(sockfd, buf, 1024) == 0) { perror("server terminated"); exit(0); } fputs(buf, stdout); } return 0; }
伺服器程序終止
客戶TCP收到FIN只是表示伺服器程序已關閉連線的服務端,從而不再往其中傳送任何資料而已。FIN的接收並沒有告知客戶TCP伺服器程序已經終止。
SIGPIPE訊號
當一個程序向某個已收到RST的套接字執行寫操作時,核心向該程序傳送一個SIGPIPE訊號。該訊號預設行為是終止程序。不論該程序捕獲該訊號並從其訊號處理函式返回,還是簡單的忽略該訊號,寫操作都將返回EPIPE。 第一次寫操作引發RST,第二次寫引發SIGPIPE訊號。寫一個已接收FIN的套接字不成問題,但寫一個已接收了RST的套接字則是一個錯誤。