生活随笔
收集整理的這篇文章主要介紹了
TCP 客户端和服务器端
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
轉自:http://blog.csdn.net/itcastcpp/article/details/39047265
前面幾篇中實現的client每次運行只能從命令行讀取一個字符串發給服務器,再從服務器收回來,現在我們把它改成交互式的,不斷從終端接受用戶輸入并和server交互。
?
[cpp]?view plain
?copy ?? #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,使它可以多次處理同一客戶端的請求。
?
[cpp]?view plain
?copy ?? #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交互。
?
[cpp]?view plain
?copy ?? #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,使它可以多次處理同一客戶端的請求。
?
[cpp]?view plain
?copy ?? #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);?? ?????????}?? }??
?
經過上面的修改后,客戶端和服務器可以進行多次交互了。
?
總結
以上是生活随笔為你收集整理的TCP 客户端和服务器端的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。