转自: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);
- }
- }
经过上面的修改后,客户端和服务器可以进行多次交互了。