TCP 客户端和服务器端

转自:http://blog.csdn.net/itcastcpp/article/details/39047265

前面几篇中实现的client每次运行只能从命令行读取一个字符串发给服务器,再从服务器收回来,现在我们把它改成交互式的,不断从终端接受用户输入并和server交互。

 

[cpp]  view plain  copy
  1. /* client.c */  
  2. #include <stdio.h>  
  3. #include <string.h>  
  4. #include <unistd.h>  
  5. #include <netinet/in.h>  
  6. #include "wrap.h"  
  7.    
  8. #define MAXLINE 80  
  9. #define SERV_PORT 8000  
  10.    
  11. int main(int argc, char *argv[])  
  12. {  
  13.          structsockaddr_in servaddr;  
  14.          charbuf[MAXLINE];  
  15.          intsockfd, n;  
  16.      
  17.          sockfd= Socket(AF_INET, SOCK_STREAM, 0);  
  18.    
  19.          bzero(&servaddr,sizeof(servaddr));  
  20.          servaddr.sin_family= AF_INET;  
  21.          inet_pton(AF_INET,"127.0.0.1", &servaddr.sin_addr);  
  22.          servaddr.sin_port= htons(SERV_PORT);  
  23.      
  24.          Connect(sockfd,(struct sockaddr *)&servaddr, sizeof(servaddr));  
  25.    
  26.          while(fgets(buf, MAXLINE, stdin) != NULL) {  
  27.                    Write(sockfd,buf, strlen(buf));  
  28.                    n= Read(sockfd, buf, MAXLINE);  
  29.                    if(n == 0)  
  30.                             printf("theother side has been closed.\n");  
  31.                    else  
  32.                             Write(STDOUT_FILENO,buf, n);  
  33.          }  
  34.    
  35.          Close(sockfd);  
  36.          return0;  
  37. }  


编译并运行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
  1. /* server.c */  
  2. #include <stdio.h>  
  3. #include <string.h>  
  4. #include <netinet/in.h>  
  5. #include "wrap.h"  
  6.    
  7. #define MAXLINE 80  
  8. #define SERV_PORT 8000  
  9.    
  10. int main(void)  
  11. {  
  12.          structsockaddr_in servaddr, cliaddr;  
  13.          socklen_tcliaddr_len;  
  14.          intlistenfd, connfd;  
  15.          charbuf[MAXLINE];  
  16.          charstr[INET_ADDRSTRLEN];  
  17.          inti, n;  
  18.    
  19.          listenfd= Socket(AF_INET, SOCK_STREAM, 0);  
  20.    
  21.          bzero(&servaddr,sizeof(servaddr));  
  22.          servaddr.sin_family= AF_INET;  
  23.          servaddr.sin_addr.s_addr= htonl(INADDR_ANY);  
  24.          servaddr.sin_port= htons(SERV_PORT);  
  25.      
  26.          Bind(listenfd,(struct sockaddr *)&servaddr, sizeof(servaddr));  
  27.    
  28.          Listen(listenfd,20);  
  29.    
  30.          printf("Acceptingconnections ...\n");  
  31.          while(1) {  
  32.                    cliaddr_len= sizeof(cliaddr);  
  33.                    connfd= Accept(listenfd,  
  34.                                      (structsockaddr *)&cliaddr, &cliaddr_len);  
  35.                    while(1) {  
  36.                             n= Read(connfd, buf, MAXLINE);  
  37.                             if(n == 0) {  
  38.                                      printf("theother side has been closed.\n");  
  39.                                      break;  
  40.                             }  
  41.                             printf("receivedfrom %s at PORT %d\n",  
  42.                                    inet_ntop(AF_INET,&cliaddr.sin_addr, str, sizeof(str)),  
  43.                                    ntohs(cliaddr.sin_port));  
  44.      
  45.                             for(i = 0; i < n; i++)  
  46.                                      buf[i]= toupper(buf[i]);  
  47.                             Write(connfd,buf, n);  
  48.                    }  
  49.                    Close(connfd);  
  50.          }  
  51. }  

 

经过上面的修改后,客户端和服务器可以进行多次交互了。

 

前面几篇中实现的client每次运行只能从命令行读取一个字符串发给服务器,再从服务器收回来,现在我们把它改成交互式的,不断从终端接受用户输入并和server交互。

 

[cpp]  view plain  copy
  1. /* client.c */  
  2. #include <stdio.h>  
  3. #include <string.h>  
  4. #include <unistd.h>  
  5. #include <netinet/in.h>  
  6. #include "wrap.h"  
  7.    
  8. #define MAXLINE 80  
  9. #define SERV_PORT 8000  
  10.    
  11. int main(int argc, char *argv[])  
  12. {  
  13.          structsockaddr_in servaddr;  
  14.          charbuf[MAXLINE];  
  15.          intsockfd, n;  
  16.      
  17.          sockfd= Socket(AF_INET, SOCK_STREAM, 0);  
  18.    
  19.          bzero(&servaddr,sizeof(servaddr));  
  20.          servaddr.sin_family= AF_INET;  
  21.          inet_pton(AF_INET,"127.0.0.1", &servaddr.sin_addr);  
  22.          servaddr.sin_port= htons(SERV_PORT);  
  23.      
  24.          Connect(sockfd,(struct sockaddr *)&servaddr, sizeof(servaddr));  
  25.    
  26.          while(fgets(buf, MAXLINE, stdin) != NULL) {  
  27.                    Write(sockfd,buf, strlen(buf));  
  28.                    n= Read(sockfd, buf, MAXLINE);  
  29.                    if(n == 0)  
  30.                             printf("theother side has been closed.\n");  
  31.                    else  
  32.                             Write(STDOUT_FILENO,buf, n);  
  33.          }  
  34.    
  35.          Close(sockfd);  
  36.          return0;  
  37. }  


编译并运行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
  1. /* server.c */  
  2. #include <stdio.h>  
  3. #include <string.h>  
  4. #include <netinet/in.h>  
  5. #include "wrap.h"  
  6.    
  7. #define MAXLINE 80  
  8. #define SERV_PORT 8000  
  9.    
  10. int main(void)  
  11. {  
  12.          structsockaddr_in servaddr, cliaddr;  
  13.          socklen_tcliaddr_len;  
  14.          intlistenfd, connfd;  
  15.          charbuf[MAXLINE];  
  16.          charstr[INET_ADDRSTRLEN];  
  17.          inti, n;  
  18.    
  19.          listenfd= Socket(AF_INET, SOCK_STREAM, 0);  
  20.    
  21.          bzero(&servaddr,sizeof(servaddr));  
  22.          servaddr.sin_family= AF_INET;  
  23.          servaddr.sin_addr.s_addr= htonl(INADDR_ANY);  
  24.          servaddr.sin_port= htons(SERV_PORT);  
  25.      
  26.          Bind(listenfd,(struct sockaddr *)&servaddr, sizeof(servaddr));  
  27.    
  28.          Listen(listenfd,20);  
  29.    
  30.          printf("Acceptingconnections ...\n");  
  31.          while(1) {  
  32.                    cliaddr_len= sizeof(cliaddr);  
  33.                    connfd= Accept(listenfd,  
  34.                                      (structsockaddr *)&cliaddr, &cliaddr_len);  
  35.                    while(1) {  
  36.                             n= Read(connfd, buf, MAXLINE);  
  37.                             if(n == 0) {  
  38.                                      printf("theother side has been closed.\n");  
  39.                                      break;  
  40.                             }  
  41.                             printf("receivedfrom %s at PORT %d\n",  
  42.                                    inet_ntop(AF_INET,&cliaddr.sin_addr, str, sizeof(str)),  
  43.                                    ntohs(cliaddr.sin_port));  
  44.      
  45.                             for(i = 0; i < n; i++)  
  46.                                      buf[i]= toupper(buf[i]);  
  47.                             Write(connfd,buf, n);  
  48.                    }  
  49.                    Close(connfd);  
  50.          }  
  51. }  

 

经过上面的修改后,客户端和服务器可以进行多次交互了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值