<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 1. 經典的回射程序 ### 1) 服務器程序srv.c ~~~ #include <stdio.h> #include <stdlib.h> #include <time.h> #include <sys/socket.h> #include <sys/types.h> #include <fcntl.h> #include <netinet/in.h> #include <errno.h> #define MAXLINE 1024 #define SA struct sockaddr void str_echo(int sockfd); int main(int argc, char **argv) { int listenfd, connfd; int buff[MAXLINE]; pid_t pid; struct sockaddr_in servaddr; struct sockaddr_in cliaddr; socklen_t cliLen; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(9877); bind(listenfd, (SA *)&servaddr, sizeof(servaddr)); listen(listenfd, 5); for ( ; ; ){ cliLen = sizeof(cliaddr); connfd = accept(listenfd, (SA *)&cliaddr, &cliLen); if ((pid = fork()) == 0){ close(listenfd); str_echo(connfd); _exit(0); } if (waitpid(pid, NULL, 0) != pid){ printf("waitpid error\n"); exit(1); } close(connfd); } return 0; } void str_echo(int sockfd) { ssize_t n; char buf[MAXLINE]; again: while ((n = read(sockfd, buf, MAXLINE)) > 0){ buf[n] = '\0'; write(sockfd, buf, n); } if (n < 0 && errno == EINTR) goto again; else if (n < 0) printf("str_echo:read error\n"); }維護子進程的信息,以便父進程在以后某個時候獲取。這些信息包括子進程的進程ID,終止狀 ~~~ ### 2) 客戶端程序cli.c ~~~ #include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <fcntl.h> #define MAXLINE 1024 #define SA struct sockaddr void str_cli(FILE *fp, int sockfd); int main(int argc, char **argv) { int sockfd, n; struct sockaddr_in servaddr; char buff[MAXLINE + 1]; struct sockaddr_in cliaddr; socklen_t cliLen; sockfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(9877); inet_pton(AF_INET, argv[1], &servaddr.sin_addr); connect(sockfd, (SA *)&servaddr, sizeof(servaddr)); str_cli(stdin, sockfd); return 0; } void str_cli(FILE *fp, int sockfd) { char sendline[MAXLINE], recvline[MAXLINE]; while (fgets(sendline, MAXLINE, fp) != NULL){ write(sockfd, sendline, strlen(sendline)); if (read(sockfd, recvline, MAXLINE) == 0){ printf("str_cli:server terminated prematurely\n"); return; } fputs(recvline, stdout); } } ~~~ ### 3)程序運行 ### (1)服務器后臺啟動 ~~~ leichaojian@ThinkPad-T430i:~$ ./srv & [1] 3932 leichaojian@ThinkPad-T430i:~$ netstat -a | grep 9877 tcp 0 0 *:9877 *:* LISTEN ~~~ ### (2)啟動客戶端,并且鍵入一行文本 客戶端: ~~~ leichaojian@ThinkPad-T430i:~$ ./cli 127.0.0.1 hello world hello world ~~~ 服務端: ~~~ leichaojian@ThinkPad-T430i:~$ netstat -a | grep 9877 tcp 0 0 *:9877 *:* LISTEN tcp 0 0 localhost:9877 localhost:43399 ESTABLISHED tcp 0 0 localhost:43399 localhost:9877 ESTABLISHED ~~~ ### (3)客戶端終止 ~~~ leichaojian@ThinkPad-T430i:~$ netstat -a | grep 9877 tcp 0 0 *:9877 *:* LISTEN tcp 0 0 localhost:43399 localhost:9877 TIME_WAIT ~~~ ## 2. 處理信號 ### 1) POSIX信號處理 ?? 信號就是告知某個進程發生了某個事件的通知,有時也稱為軟件中斷。信號通常是異步發生的,也就是說進程預先不知道信號的準確發生時刻。 ?? 信號可以: (1)由一個進程發給另一個進程。 (2)由內核發給某個進程。 ?? 每個信號都有一個與之關聯的處置,也稱為行為: (1)我們可以提供一個函數,只要有特定信號發生它就會被調用。這樣的函數稱為信號處理函數,這種行為稱為捕獲信號。有兩個信號不能被捕獲,它們是SIGKILL和SIGSTOP。信號處理函數由信號值這個單一的整數參數來調用,且沒有返回值,其函數原型如下: ~~~ void handler( int signo ); ~~~ (2)我們可以把某個信號的處置設定為SIG_IGN來忽略它。SIGKILL和SIGSTOP這兩個信號不能被忽略。 (3)我們可以把某個信號的處置設定為SIG_DFL來啟用它的默認處置。 ### 2) 處理SIGCHLD信號 ?? 設置僵尸狀態的目的是維護子進程的信息,以便父進程在以后某個時候獲取。這些信息包括子進程的進程ID,終止狀態以及資源利用信息。如果一個進程終止,而該進程有子進程處于僵尸狀態,那么它的所有僵尸子進程的父進程ID將被重置為1(init進程)。繼承這些子進程的init進程將清理它們。 ?? 而僵尸進程出現時間是在子進程終止后,但是父進程尚未讀取這些數據之前。所有解決之道就是保證父進程處理這些數據,我們可以通過wait或者waitpid函數來達到這個要求。 ?? 由于子進程的終止必然會產生信號SIGCHLD信號,所以重寫TCP服務器程序最終版本: ### (1)服務器程序srv.c ~~~ #include <stdio.h> #include <netinet/in.h> #include <stdlib.h> #include <sys/socket.h> #include <signal.h> #include <errno.h> #define MAXLINE 1024 #define SA struct sockaddr void sig_chld(int signo); typedef void Sigfunc(int); Sigfunc *Signal(int signo, Sigfunc *func); void str_echo(int sockfd); int main(int argc, char **argv) { int listenfd, connfd; pid_t childpid; socklen_t clilen; struct sockaddr_in servaddr, cliaddr; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(9877); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); bind(listenfd, (SA *)&servaddr, sizeof(servaddr)); listen(listenfd, 5); Signal(SIGCHLD, sig_chld); for ( ; ; ){ clilen = sizeof(cliaddr); if ((connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0){ if (errno == EINTR) continue; else{ printf("accept error\n"); exit(-1); } } if ((childpid = fork()) == 0){ close(listenfd); str_echo(connfd); exit(0); } close(connfd); } return 0; } void sig_chld(int signo) { pid_t pid; int stat; while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) printf("child %d terminated\n", pid); return; } Sigfunc *Signal(int signo, Sigfunc *func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (signo == SIGALRM){ #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; #endif } else { #ifdef SA_RESTART act.sa_flags |= SA_RESTART; #endif } if (sigaction(signo, &act, &oact) < 0) return (SIG_ERR); return (oact.sa_handler); } void str_echo(int sockfd) { char buff[MAXLINE]; int n; for ( ; ; ){ if ((n = read(sockfd, buff, MAXLINE)) > 0){ buff[n] = '\0'; write(sockfd, buff, n); } else if (n < 0 && errno == EINTR) continue; else if (n < 0){ printf("str_echo:read error\n"); return; } else if (n == 0){ break; } } } ~~~ ### (2)客戶端測試程序cli.c ~~~ #include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <fcntl.h> #define MAXLINE 1024 #define SA struct sockaddr void str_cli(FILE *fp, int sockfd); int main(int argc, char **argv) { int sockfd[5], n; struct sockaddr_in servaddr; struct sockaddr_in cliaddr; int i; for (i = 0; i < 5; i++){ sockfd[i] = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(9877); inet_pton(AF_INET, argv[1], &servaddr.sin_addr); connect(sockfd[i], (SA *)&servaddr, sizeof(servaddr)); } str_cli(stdin, sockfd[0]); return 0; } void str_cli(FILE *fp, int sockfd) { char sendline[MAXLINE], recvline[MAXLINE]; while (fgets(sendline, MAXLINE, fp) != NULL){ write(sockfd, sendline, strlen(sendline)); if (read(sockfd, recvline, MAXLINE) == 0){ printf("str_cli:server terminated prematurely\n"); return; } fputs(recvline, stdout); } } ~~~ 程序輸出如下: 客戶端: ~~~ leichaojian@ThinkPad-T430i:~$ ./cli 127.0.0.1 hello world hello world ^C ~~~ 服務端: ~~~ leichaojian@ThinkPad-T430i:~$ ./srv child 9831 terminated child 9835 terminated child 9832 terminated child 9833 terminated child 9834 terminated ^C ~~~ ### 3) 測試僵尸進程的產生 test.c: ~~~ #include <stdio.h> #include <signal.h> #include <sys/wait.h> int main( void ) { pid_t pid; if ( ( pid = fork() ) == 0 ){ printf("child:%d\n", getpid()); exit(0); } sleep( 20 ); if ( pid > 0 ){ printf("parent:%d\n", getpid() ); } return 0; } ~~~ 程序運行: ~~~ leichaojian@ThinkPad-T430i:~$ ./a.out child:14447 parent:14446 ~~~ 在顯示child:14447而尚未顯示parent:14446(即20秒的睡眠時間),我們執行如下命令: ~~~ leichaojian@ThinkPad-T430i:~$ ps -eo state,pid,cmd | grep '^Z' Z 14447 [a.out] <defunct> ~~~ ?? 發現子進程14447果真稱為僵尸進程。但是過了20秒后,再次執行時候,則沒有任何數據,說明僵尸進程已經被父進程殺死了(就是父進程讀取了子進程的數據) ### 4) 服務器進程終止 具體操作如下: ### 1)運行服務器程序,運行客戶端程序: 服務端: ~~~ leichaojian@ThinkPad-T430i:~$ ./tcpserv ~~~ 客戶端: ~~~ leichaojian@ThinkPad-T430i:~$ ./tcpcli 127.0.0.1 hello hello ~~~ 監視端: ~~~ leichaojian@ThinkPad-T430i:~$ netstat -a | grep 9877 tcp 0 0 *:9877 *:* LISTEN tcp 0 0 localhost:37935 localhost:9877 ESTABLISHED tcp 0 0 localhost:9877 localhost:37935 ESTABLISHED ~~~ ### 2) 終止服務器程序(先終止服務器程序,然后執行監視端,再執行客戶端,再執行監視端) 監視端: ~~~ leichaojian@ThinkPad-T430i:~$ netstat -a | grep 9877 tcp 0 0 localhost:9877 localhost:37953 FIN_WAIT2 tcp 1 0 localhost:37953 localhost:9877 CLOSE_WAIT ~~~ 客戶端: ~~~ leichaojian@ThinkPad-T430i:~$ ./tcpcli 127.0.0.1 hello hello world str_cli error ~~~ 監視端:(無任何輸出,說明客戶端進程已經終止,這里終止是產生了信號,強行終止) 來自UNP上的解釋是:當一個進程向某個已收到RST的套接字執行寫操作時,內核向該進程發送一個SIGPIPE信號。該信號的默認行為是終止進程,因此進程必須捕獲它以免不情愿的被終止。 ~~~ leichaojian@ThinkPad-T430i:~$ netstat -a | grep 9877 ~~~ ### 3) 問題出在哪里? ?? 當服務端的FIN到達套接字時,客戶正阻塞與fgets調用上。客戶實際上在應對兩個描述符--套接字和用戶輸入,它不能單純阻塞在這兩個源中的某個特定源的輸入上(正如目前編寫的str_cli函數所為),而是應該阻塞在其中任何一個源的輸入上,這正是select和poll這兩個函數的目的之一。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看