日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UNIX网络编程笔记(4):简单的回射程序

發(fā)布時間:2023/11/30 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UNIX网络编程笔记(4):简单的回射程序 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上一講中我們通過調(diào)用fork函數(shù)實現(xiàn)了一個簡單的并發(fā)時間獲取服務(wù)器。這是一個簡單的并發(fā)服務(wù)器框架,然而這里使用這個框架實現(xiàn)一個簡單的回射服務(wù)器會出現(xiàn)一個問題,這個問題就是僵尸子進(jìn)程。

1、回射程序

下圖是這個回射程序的主要結(jié)構(gòu):


客戶向服務(wù)器發(fā)送一行文字,服務(wù)器回射回來,然后客戶再輸出到屏幕上。這里的fgets、fputs、read、write函數(shù)都是標(biāo)準(zhǔn)庫函數(shù)或系統(tǒng)調(diào)用,唯一要說明的就是這個readline函數(shù),這個函數(shù)讀取文件中的一行,調(diào)用read系統(tǒng)調(diào)用:

int readline(int fd,void *vptr,size_t maxlen) {ssize_t n,rc;char c,*ptr;ptr=vptr;for(n=1;n<maxlen;n++){again:if((rc=read(fd,&c,1))==1){*ptr++=c;if(c=='\n')break;}else if(rc==0){*ptr=0;return (n-1);}else{if(errno==EINTR)goto again;return (-1);}}*ptr=0;return(n); }這里的服務(wù)器程序還是遵循上一講中的流程,不同的是處理請求的過程,服務(wù)器處理請求時調(diào)用一個函數(shù)str_echo,返回客戶寫入的一行,代碼如下:

#include <sys/socket.h> #include <errno.h> #include <string.h> #include <strings.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <time.h> #include <unistd.h> #define MAXLINE 1024 void str_echo(int sockfd); int main(int argc,char *argv[]) {int listenfd;struct sockaddr_in servaddr;char buff[MAXLINE];time_t ticks;if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0){printf("socket error\n");return 0;}bzero(&servaddr,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(5000);servaddr.sin_addr.s_addr=htonl(INADDR_ANY);if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0){printf("bind error\n");return 0;}if(listen(listenfd,5)<0){printf("listen error\n");return 0;}int connfd;socklen_t len;struct sockaddr_in cliaddr;pid_t pid;for(;;){len=sizeof(cliaddr);if((connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&len))<0){printf("accept error\n");return 0;}if((pid=fork())==0){if(close(listenfd)<0){printf("close listenfd error\n");return 0;}printf("[PID]%ld Receive a connection from:%s.%d\n",(long)getpid(),inet_ntop(AF_INET,&cliaddr.sin_addr,buff,sizeof(buff)),ntohs(cliaddr.sin_port));str_echo(connfd);if(close(connfd)<0){printf("close child connfd error\n");return 0;}return 0;}if(close(connfd)<0){printf("close parent connfd error\n");return 0;}} }void str_echo(int sockfd) {ssize_t n;char buf[MAXLINE]; again:while((n=read(sockfd,buf,MAXLINE))>0)write(sockfd,buf,n);if(n<0&&errno==EINTR)goto again;else if(n<0){printf("read error\n");return;} }客戶端程序也大體相同,首先從標(biāo)準(zhǔn)輸入中讀取一行,寫入套接字發(fā)送給服務(wù)器,接收到套接字后將套接字的內(nèi)容輸出到標(biāo)準(zhǔn)輸出上,代碼如下:

#include <sys/socket.h> #include <errno.h> #include <stdio.h> #include <string.h> #include <strings.h> #include <netinet/in.h> #include <arpa/inet.h> #define MAXLINE 1024 void str_cli(FILE *fp,int sockfd); int readline(int fd,void *vptr,size_t maxlen); int main(int argc,char *argv[]) {int sockfd;char recvline[MAXLINE];if(argc!=2||strcmp(argv[1],"--help")==0){printf("Usage:%s <IPaddress>\n",argv[0]);return 0;}if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0){printf("socket error\n");return 0;}struct sockaddr_in servaddr;bzero(&servaddr,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(5000);if(inet_pton(AF_INET,argv[1],&servaddr.sin_addr)<=0){printf("inet_pton error for %s\n",argv[1]);return 0;}if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0){printf("connect error\n");return 0;}str_cli(stdin,sockfd);return 0; } void str_cli(FILE *fp,int sockfd) {char sendline[MAXLINE],recvline[MAXLINE];printf("puts:");while(fgets(sendline,MAXLINE,fp)){write(sockfd,sendline,strlen(sendline));if(readline(sockfd,recvline,MAXLINE)==0){printf("str_cli:server terminated prematurely\n");return;}printf("gets:");fputs(recvline,stdout);printf("puts:");} } int readline(int fd,void *vptr,size_t maxlen) {ssize_t n,rc;char c,*ptr;ptr=vptr;for(n=1;n<maxlen;n++){again:if((rc=read(fd,&c,1))==1){*ptr++=c;if(c=='\n')break;}else if(rc==0){*ptr=0;return (n-1);}else{if(errno==EINTR)goto again;return (-1);}}*ptr=0;return(n); }下面是運(yùn)行結(jié)果:

(1)啟動服務(wù)器:


(2)客戶發(fā)起請求并輸入文字:


服務(wù)器成功返回并輸出到標(biāo)準(zhǔn)輸出上。

(3)服務(wù)器端:


2、程序執(zhí)行時發(fā)生了什么?

1、啟動時

(1)服務(wù)器啟動后阻塞在accept函數(shù)上,等待客戶連接。客戶發(fā)起連接后accept函數(shù)返回,服務(wù)器進(jìn)入子進(jìn)程調(diào)用str_echo函數(shù),函數(shù)調(diào)用read系統(tǒng)調(diào)用,并阻塞在這里,因為客戶還沒有輸入文字;

(2)客戶建立連接后,調(diào)用str_cli函數(shù),函數(shù)調(diào)用fgets并阻塞,等待用戶輸入;

(3)與此同時,服務(wù)器父進(jìn)程關(guān)閉連接描述符connfd,重新阻塞在accept函數(shù)上,等待下一個客戶連接;

以上就是連接建立后發(fā)生的事情。

2、執(zhí)行時

(1)執(zhí)行時用戶在客戶端輸入文字,fgets函數(shù)返回,str_cli函數(shù)將輸入寫入套接字,發(fā)送給服務(wù)器子進(jìn)程,然后調(diào)用readline,readline函數(shù)調(diào)用read,并阻塞于read,因為客戶等待服務(wù)器的應(yīng)答;

(2)服務(wù)器子進(jìn)程調(diào)用read讀取客戶輸入,寫入套接字發(fā)送給客戶作為應(yīng)答并繼續(xù)阻塞在read函數(shù)上;

(3)客戶調(diào)用readline函數(shù)讀取服務(wù)器子進(jìn)程應(yīng)答并將數(shù)據(jù)寫入標(biāo)準(zhǔn)輸出;

(4)循環(huán)進(jìn)行直到客戶得到用戶的EOF輸入;

3、終止時

(1)用戶在客戶輸入EOF字符,fgets返回NULL指針,于是str_cli函數(shù)返回;

(2)str_cli函數(shù)返回到main函數(shù),main然后通過return終止;

(3)進(jìn)程終止后關(guān)閉所有打開的描述符,因此客戶打開的套接字由內(nèi)核關(guān)閉,導(dǎo)致客戶TCP發(fā)送一個FIN給服務(wù)器,服務(wù)器以ACK響應(yīng),這就是TCP終止的前半部分;

(4)當(dāng)服務(wù)器接收到FIN時,服務(wù)器子進(jìn)程阻塞于readline函數(shù),readline函數(shù)返回0,導(dǎo)致str_echo函數(shù)返回到main;

(5)服務(wù)器子進(jìn)程通過return終止;

(6)服務(wù)器子進(jìn)程打開的所有描述符關(guān)閉。由子進(jìn)程來關(guān)閉已連接套接字會引發(fā)TCP連接終止序列的最后兩個分節(jié):一個從服務(wù)器到客戶的FIN和客戶到服務(wù)器的ACK。至此,連接完全終止;

3、有什么問題?

上面的進(jìn)程終止還有一部分內(nèi)容,就是服務(wù)器子進(jìn)程終止時,會給父進(jìn)程發(fā)送一個SIGCHLD信號。這里我們沒有捕獲信號,而該信號的默認(rèn)行為是被忽略。由于父進(jìn)程中沒有處理該信號,子進(jìn)程于是進(jìn)入僵死狀態(tài),即僵尸進(jìn)程。可以使用ps命令查看:


可以看到,進(jìn)程號是4228的進(jìn)程是僵尸進(jìn)程,而在我們的服務(wù)器運(yùn)行時,客戶連接的子進(jìn)程ID正好是4228。

僵尸進(jìn)程必須處理。下一講來用信號處理函數(shù)處理僵尸進(jìn)程。

總結(jié)

以上是生活随笔為你收集整理的UNIX网络编程笔记(4):简单的回射程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。