轉自:http://blog.csdn.net/itcastcpp/article/details/39047265
前面幾篇中實現的client每次運行只能從命令行讀取一個字符串發給服務器,再從服務器收回來,現在我們把它改成交互式的,不斷從終端接受用戶輸入并和server交互。
?
- ??
- #include?<stdio.h>??
- #include?<string.h>??
- #include?<unistd.h>??
- #include?<netinet/in.h>??
- #include?"wrap.h"??
- ???
- #define?MAXLINE?80??
- #define?SERV_PORT?8000??
- ???
- int?main(int?argc,?char?*argv[])??
- {??
- ?????????structsockaddr_in?servaddr;??
- ?????????charbuf[MAXLINE];??
- ?????????intsockfd,?n;??
- ?????
- ?????????sockfd=?Socket(AF_INET,?SOCK_STREAM,?0);??
- ???
- ?????????bzero(&servaddr,sizeof(servaddr));??
- ?????????servaddr.sin_family=?AF_INET;??
- ?????????inet_pton(AF_INET,"127.0.0.1",?&servaddr.sin_addr);??
- ?????????servaddr.sin_port=?htons(SERV_PORT);??
- ?????
- ?????????Connect(sockfd,(struct?sockaddr?*)&servaddr,?sizeof(servaddr));??
- ???
- ?????????while(fgets(buf,?MAXLINE,?stdin)?!=?NULL)?{??
- ???????????????????Write(sockfd,buf,?strlen(buf));??
- ???????????????????n=?Read(sockfd,?buf,?MAXLINE);??
- ???????????????????if(n?==?0)??
- ????????????????????????????printf("theother?side?has?been?closed.\n");??
- ???????????????????else??
- ????????????????????????????Write(STDOUT_FILENO,buf,?n);??
- ?????????}??
- ???
- ?????????Close(sockfd);??
- ?????????return0;??
- }??
編譯并運行server和client,看看是否達到了你預想的結果。
?
這時server仍在運行,但是client的運行結果并不正確。原因是什么呢?仔細查看server.c可以發現,server對每個請求只處理一次,應答后就關閉連接,client不能繼續使用這個連接發送數據。但是client下次循環時又調用write發數據給server,write調用只負責把數據交給TCP發送緩沖區就可以成功返回了,所以不會出錯,而server收到數據后應答一個RST段,client收到RST段后無法立刻通知應用層,只把這個狀態保存在TCP協議層。client下次循環又調用write發數據給server,由于TCP協議層已經處于RST狀態了,因此不會將數據發出,而是發一個SIGPIPE信號給應用層,SIGPIPE信號的缺省處理動作是終止程序,所以看到上面的現象。
?
為了避免client異常退出,上面的代碼應該在判斷對方關閉了連接后break出循環,而不是繼續write。另外,有時候代碼中需要連續多次調用write,可能還來不及調用read得知對方已關閉了連接就被SIGPIPE信號終止掉了,這就需要在初始化時調用sigaction處理SIGPIPE信號,如果SIGPIPE信號沒有導致進程異常退出,write返回-1并且errno為EPIPE。
?
另外,我們需要修改server,使它可以多次處理同一客戶端的請求。
?
- ??
- #include?<stdio.h>??
- #include?<string.h>??
- #include?<netinet/in.h>??
- #include?"wrap.h"??
- ???
- #define?MAXLINE?80??
- #define?SERV_PORT?8000??
- ???
- int?main(void)??
- {??
- ?????????structsockaddr_in?servaddr,?cliaddr;??
- ?????????socklen_tcliaddr_len;??
- ?????????intlistenfd,?connfd;??
- ?????????charbuf[MAXLINE];??
- ?????????charstr[INET_ADDRSTRLEN];??
- ?????????inti,?n;??
- ???
- ?????????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(SERV_PORT);??
- ?????
- ?????????Bind(listenfd,(struct?sockaddr?*)&servaddr,?sizeof(servaddr));??
- ???
- ?????????Listen(listenfd,20);??
- ???
- ?????????printf("Acceptingconnections?...\n");??
- ?????????while(1)?{??
- ???????????????????cliaddr_len=?sizeof(cliaddr);??
- ???????????????????connfd=?Accept(listenfd,??
- ?????????????????????????????????????(structsockaddr?*)&cliaddr,?&cliaddr_len);??
- ???????????????????while(1)?{??
- ????????????????????????????n=?Read(connfd,?buf,?MAXLINE);??
- ????????????????????????????if(n?==?0)?{??
- ?????????????????????????????????????printf("theother?side?has?been?closed.\n");??
- ?????????????????????????????????????break;??
- ????????????????????????????}??
- ????????????????????????????printf("receivedfrom?%s?at?PORT?%d\n",??
- ???????????????????????????????????inet_ntop(AF_INET,&cliaddr.sin_addr,?str,?sizeof(str)),??
- ???????????????????????????????????ntohs(cliaddr.sin_port));??
- ?????
- ????????????????????????????for(i?=?0;?i?<?n;?i++)??
- ?????????????????????????????????????buf[i]=?toupper(buf[i]);??
- ????????????????????????????Write(connfd,buf,?n);??
- ???????????????????}??
- ???????????????????Close(connfd);??
- ?????????}??
- }??
?
經過上面的修改后,客戶端和服務器可以進行多次交互了。
?
前面幾篇中實現的client每次運行只能從命令行讀取一個字符串發給服務器,再從服務器收回來,現在我們把它改成交互式的,不斷從終端接受用戶輸入并和server交互。
?
- ??
- #include?<stdio.h>??
- #include?<string.h>??
- #include?<unistd.h>??
- #include?<netinet/in.h>??
- #include?"wrap.h"??
- ???
- #define?MAXLINE?80??
- #define?SERV_PORT?8000??
- ???
- int?main(int?argc,?char?*argv[])??
- {??
- ?????????structsockaddr_in?servaddr;??
- ?????????charbuf[MAXLINE];??
- ?????????intsockfd,?n;??
- ?????
- ?????????sockfd=?Socket(AF_INET,?SOCK_STREAM,?0);??
- ???
- ?????????bzero(&servaddr,sizeof(servaddr));??
- ?????????servaddr.sin_family=?AF_INET;??
- ?????????inet_pton(AF_INET,"127.0.0.1",?&servaddr.sin_addr);??
- ?????????servaddr.sin_port=?htons(SERV_PORT);??
- ?????
- ?????????Connect(sockfd,(struct?sockaddr?*)&servaddr,?sizeof(servaddr));??
- ???
- ?????????while(fgets(buf,?MAXLINE,?stdin)?!=?NULL)?{??
- ???????????????????Write(sockfd,buf,?strlen(buf));??
- ???????????????????n=?Read(sockfd,?buf,?MAXLINE);??
- ???????????????????if(n?==?0)??
- ????????????????????????????printf("theother?side?has?been?closed.\n");??
- ???????????????????else??
- ????????????????????????????Write(STDOUT_FILENO,buf,?n);??
- ?????????}??
- ???
- ?????????Close(sockfd);??
- ?????????return0;??
- }??
編譯并運行server和client,看看是否達到了你預想的結果。
?
這時server仍在運行,但是client的運行結果并不正確。原因是什么呢?仔細查看server.c可以發現,server對每個請求只處理一次,應答后就關閉連接,client不能繼續使用這個連接發送數據。但是client下次循環時又調用write發數據給server,write調用只負責把數據交給TCP發送緩沖區就可以成功返回了,所以不會出錯,而server收到數據后應答一個RST段,client收到RST段后無法立刻通知應用層,只把這個狀態保存在TCP協議層。client下次循環又調用write發數據給server,由于TCP協議層已經處于RST狀態了,因此不會將數據發出,而是發一個SIGPIPE信號給應用層,SIGPIPE信號的缺省處理動作是終止程序,所以看到上面的現象。
?
為了避免client異常退出,上面的代碼應該在判斷對方關閉了連接后break出循環,而不是繼續write。另外,有時候代碼中需要連續多次調用write,可能還來不及調用read得知對方已關閉了連接就被SIGPIPE信號終止掉了,這就需要在初始化時調用sigaction處理SIGPIPE信號,如果SIGPIPE信號沒有導致進程異常退出,write返回-1并且errno為EPIPE。
?
另外,我們需要修改server,使它可以多次處理同一客戶端的請求。
?
- ??
- #include?<stdio.h>??
- #include?<string.h>??
- #include?<netinet/in.h>??
- #include?"wrap.h"??
- ???
- #define?MAXLINE?80??
- #define?SERV_PORT?8000??
- ???
- int?main(void)??
- {??
- ?????????structsockaddr_in?servaddr,?cliaddr;??
- ?????????socklen_tcliaddr_len;??
- ?????????intlistenfd,?connfd;??
- ?????????charbuf[MAXLINE];??
- ?????????charstr[INET_ADDRSTRLEN];??
- ?????????inti,?n;??
- ???
- ?????????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(SERV_PORT);??
- ?????
- ?????????Bind(listenfd,(struct?sockaddr?*)&servaddr,?sizeof(servaddr));??
- ???
- ?????????Listen(listenfd,20);??
- ???
- ?????????printf("Acceptingconnections?...\n");??
- ?????????while(1)?{??
- ???????????????????cliaddr_len=?sizeof(cliaddr);??
- ???????????????????connfd=?Accept(listenfd,??
- ?????????????????????????????????????(structsockaddr?*)&cliaddr,?&cliaddr_len);??
- ???????????????????while(1)?{??
- ????????????????????????????n=?Read(connfd,?buf,?MAXLINE);??
- ????????????????????????????if(n?==?0)?{??
- ?????????????????????????????????????printf("theother?side?has?been?closed.\n");??
- ?????????????????????????????????????break;??
- ????????????????????????????}??
- ????????????????????????????printf("receivedfrom?%s?at?PORT?%d\n",??
- ???????????????????????????????????inet_ntop(AF_INET,&cliaddr.sin_addr,?str,?sizeof(str)),??
- ???????????????????????????????????ntohs(cliaddr.sin_port));??
- ?????
- ????????????????????????????for(i?=?0;?i?<?n;?i++)??
- ?????????????????????????????????????buf[i]=?toupper(buf[i]);??
- ????????????????????????????Write(connfd,buf,?n);??
- ???????????????????}??
- ???????????????????Close(connfd);??
- ?????????}??
- }??
?
經過上面的修改后,客戶端和服務器可以進行多次交互了。
?