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. }  

 

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

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384405.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

利用多线程实现linux下C语言的聊天室程序:

转载&#xff1a;http://www.360doc.com/content/16/0421/11/478627_552531090.shtml 利用多线程实现linux下C语言的聊天室程序&#xff1a; 客户端代码&#xff1a; threadsend线程负责客户端消息的发送&#xff1b; threadrecv线程负责客户端接受服务器端的消息。 [html] v…

Linux系统编程(七)消息队列

Linux系统编程&#xff08;七&#xff09;消息队列一、什么是消息队列二、消息队列内部原理三、实现消息队列的收发1.发送消息队列2.接收消息队列四、消息队列与命名管道的比较一、什么是消息队列 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。每个数据块都…

基于Linux的SOCKET编程之TCP半双工Client-Server聊天程序

转自&#xff1a;http://blog.csdn.net/apollon_krj/article/details/53398448#0-tsina-1-64987-397232819ff9a47a7b7e80a40613cfe1 所谓半双工通信&#xff0c;即通信双方都可以实现接发数据&#xff0c;但是有一个限制&#xff1a;只能一方发一方收&#xff0c;之后交换收发对…

Linux系统编程(八)线程

Linux系统编程&#xff08;八&#xff09;线程一、什么是线程&#xff1f;二、Linux内核线程实现原理线程共享资源线程非共享资源线程优缺点线程控制原语一、什么是线程&#xff1f; LWP&#xff1a;light weight process 轻量级的进程&#xff0c;本质仍是进程(在Linux环境下…

智能算法(GA、DBO等)求解阻塞流水车间调度问题(BFSP)

先做一个声明&#xff1a;文章是由我的个人公众号中的推送直接复制粘贴而来&#xff0c;因此对智能优化算法感兴趣的朋友&#xff0c;可关注我的个人公众号&#xff1a;启发式算法讨论。我会不定期在公众号里分享不同的智能优化算法&#xff0c;经典的&#xff0c;或者是近几年…

Linux socket编程,对套接字进行封装

转自&#xff1a;http://www.cnblogs.com/-Lei/archive/2012/09/04/2670942.html 下面是对socket操作的封装&#xff0c;因为在Linux下写中文到了windows里面会乱码&#xff0c;所以注释用英文来写&#xff0c;有空再查下解决方法吧 socket.h #ifndef SOCKET_H #define SOCKET_…

Linux系统编程(九)线程同步

Linux系统编程&#xff08;九&#xff09;线程同步一、什么是线程同步&#xff1f;二、互斥量三、条件变量pthread_cond_wait函数pthread_cond_signal函数生产者和消费者模型一、什么是线程同步&#xff1f; 线程同步&#xff0c;指一个线程发出某一功能调用时&#xff0c;在没…

linux网络编程(一)网络基础传输知识

linux网络编程&#xff08;一&#xff09;网络传输基础知识一、什么是协议&#xff1f;二、使用步骤典型协议2.网络应用程序设计模式C/S模式B/S模式优缺点3.分层模型4.TCP/IP四层模型通信过程5.协议格式数据包封装以太网帧格式ARP数据报格式IP段格式UDP数据报格式TCP数据报格式…

linux网络编程:使用多进程实现socket同时收发数据

转载&#xff1a;http://blog.csdn.net/li_wen01/article/details/52685844 前面已讲过使用一个进程实现服务端和客户端P2P通信的实例&#xff0c;但是它只能同时处理一个客户端的连接。如果要实现并发处理多个客户端的连接并且实现P2P通信&#xff0c;可以使用多进程来处理。相…

Linux 进程学习(四)------ sigaction 函数

转自&#xff1a;http://www.cnblogs.com/wblyuyang/archive/2012/11/13/2768923.html 使用 sigaction 函数&#xff1a; signal 函数的使用方法简单&#xff0c;但并不属于 POSIX 标准&#xff0c;在各类 UNIX 平台上的实现不尽相同&#xff0c;因此其用途受 到了一定的限制…

linux网络编程(二)高并发服务器

linux网络编程&#xff08;二&#xff09;高并发服务器错误处理高并发服务器多进程并发服务器客户端错误处理 #include "wrap.h"int Bind(int fd, const struct sockaddr* sa, socklen_t salen) {int ret;if ((ret bind(fd, sa, salen)) < 0){perror("bind…

linux知识(一) 程序、进程与线程

linux知识&#xff08;一&#xff09; 程序、进程与线程程序进程程序如何变成进程&#xff1f;线程线程与进程fork和创建新线程的区别优点程序 程序&#xff1a;程序是已编译好的二进制文件&#xff0c;存储在磁盘中&#xff0c;不占用系统资源 程序包括&#xff1a; RO段&am…

linux 信号signal和sigaction理解

转载&#xff1a;http://blog.csdn.net/beginning1126/article/details/8680757 今天看到unp时发现之前对signal到理解实在浅显&#xff0c;今天拿来单独学习讨论下。 signal&#xff0c;此函数相对简单一些&#xff0c;给定一个信号&#xff0c;给出信号处理函数则可&#xff…

linux知识(二)互斥量、信号量和生产者消费者模型

linux知识&#xff08;二&#xff09;互斥量、信号量和生产者消费者模型一、互斥量产生原因二、信号量生产者消费者模型一、互斥量 产生原因 使用多线程常常会碰到数据混乱的问题&#xff0c;那么使用互斥量&#xff0c;相当于“加锁”的操作&#xff0c;将有助于解决数据混乱…

基于Linux的Socket编程之TCP全双工Server-Client聊天程序

转载&#xff1a;http://blog.csdn.net/apollon_krj/article/details/53437764#0-tsina-1-58570-397232819ff9a47a7b7e80a40613cfe1 一、引言&#xff1a; 由于accept函数、read、write、recv、send等函数都是是阻塞式的&#xff0c;在同一个进程之中&#xff0c;只要有任何一个…

数据结构(一)线性表

数据结构&#xff08;一&#xff09;线性表一、线性表定义二、顺序表定义动态数组三、单链表定义不带头结点带头结点头结点与不带头结点的区别头插法与尾插法双链表循环链表循环单链表循环双链表静态链表一、线性表定义 线性表是具有相同数据类型的n个数据元素的有限序列 特点…

linux网络编程(二)TCP通讯状态

linux网络编程&#xff08;二&#xff09;TCP通讯状态TCP状态转换为什么需要等待2MSL&#xff1f;端口复用TCP状态转换 tcp协议连接开始会经过三次握手&#xff0c;客户端和服务器开始都会处于CLOSED状态 第一次握手&#xff1a;客户端会先发送SYN请求给服务器&#xff0c;客户…

gethostbyname() 函数说明

转载&#xff1a;http://www.cnblogs.com/cxz2009/archive/2010/11/19/1881611.html gethostbyname()函数说明——用域名或主机名获取IP地址 包含头文件 #include <netdb.h> #include <sys/socket.h> 函数原型 struct hostent *gethostbyna…

linux网络编程(三)select、poll和epoll

linux网络编程&#xff08;三&#xff09;select、poll和epoll一、为什么会有多路I/O转接服务器&#xff1f;二、select三、poll三、epoll一、为什么会有多路I/O转接服务器&#xff1f; 为什么会有多路I/O转接服务器呢&#xff1f;在学这个之前&#xff0c;我们同使用的是多线…

Linux socket编程(一) 对套接字操作的封装

转载:http://www.cnblogs.com/-Lei/archive/2012/09/04/2670942.html 以前写的&#xff0c;现在回顾一下&#xff1a; 下面是对socket操作的封装&#xff0c;因为在Linux下写中文到了windows里面会乱码&#xff0c;所以注释用英文来写&#xff0c;有空再查下解决方法吧 socket.…