转载:http://blog.csdn.net/apollon_krj/article/details/53437764#0-tsina-1-58570-397232819ff9a47a7b7e80a40613cfe1

一、引言:

由于accept函数、read、write、recv、send等函数都是是阻塞式的,在同一个进程之中,只要有任何一个函数没有执行完毕,处于阻塞状态,之后的函数与功能就不能处理,很难实现点对点的Server-Client全双工通信。因为全双工通信是非阻塞式的通信方式,即使对方没有回复消息,都可以随时发送。如果只是电报机式的半双工通信,之前已经基本实现:基于Linux的SOCKET编程之TCP半双工Client-Server聊天程序 
而对于QQ点对点聊天式的全双工通信,又该怎样实现呢?对于当前所学只能想到使用fork函数创建一个子进程,其中父进程用来处理发(或者收),而子进程用来处理收(或者发)的过程。fork函数的一些基本的使用可参照:进程创建与fork()的恩怨情仇

二、测试代码:

测试环境(Redhat 6.4)

1、客户端(Client):

# include<stdio.h>
# include<stdlib.h>
# include<string.h>
# include<unistd.h>
# include<sys/socket.h>
# include<arpa/inet.h>
# include<netinet/in.h>
# include<signal.h># define MAX_BUF_LEN 128/*处理系统调用中产生的错误*/
void error_print(char * ptr)
{perror(ptr);exit(EXIT_FAILURE);
}
/*处理通信结束时回调函数接收到的信号*/
void quit_tranmission(int sig)
{printf("recv a quit signal = %d\n",sig);exit(EXIT_SUCCESS);
}
int main(void)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0)error_print("socket");struct sockaddr_in servaddr;bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = 1234;servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");/*inet_aton("127.0.0.1",&servaddr.sin_addr);*/int conn;if((conn = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) < 0)error_print("connect");pid_t pid;pid = fork();if(pid == -1){error_print("fork");}if(pid == 0){char recv_buf[MAX_BUF_LEN] = {0};while(1){bzero(recv_buf,sizeof(recv_buf));int ret = read(sockfd, recv_buf, sizeof(recv_buf));if(ret == -1)error_print("read");else if(ret == 0){printf("server is close!\n");break;//子进程收到服务器端退出的信息(服务器Ctrl+C结束通信进程,read函数返回值为0,退出循环)}fputs(recv_buf,stdout);/*将收到的信息输出到标准输出stdout上*/}close(sockfd);/*子进程退出,通信结束关闭套接字*/kill(getppid(),SIGUSR1);/*子进程结束,也要向父进程发出一个信号告诉父进程终止接收,否则父进程一直会等待输入*/exit(EXIT_SUCCESS);/*子进程正常退出结束,向父进程返回EXIT_SUCCESS*/}else{signal(SIGUSR1,quit_tranmission);/*回调函数处理通信中断*/char send_buf[MAX_BUF_LEN] = {0};/*如果服务器Ctrl+C结束通信进程,fgets获取的就是NULL,否则就进入循环正常发送数据*/while(fgets(send_buf,sizeof(send_buf), stdin) != NULL){int set = write(sockfd, send_buf, strlen(send_buf));/*将send_buf缓冲区的数据发送给对端服务器*/if(set < 0)error_print("write");bzero(send_buf,strlen(send_buf));}close(sockfd);/*通信结束,关闭套接字*/}return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

2、服务器(Server):

# include<stdio.h>
# include<stdlib.h>
# include<string.h>
# include<unistd.h>
# include<sys/socket.h>
# include<arpa/inet.h>
# include<netinet/in.h>
# include<signal.h># define MAX_BUF_LEN 128void error_print(char * ptr)
{perror(ptr);exit(EXIT_FAILURE);
}void quit_tranmission(int sig)
{printf("recv a quit signal = %d\n",sig);exit(EXIT_SUCCESS);
}
int main(void)
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);/*IPV4流式协议即TCP协议*/if(sockfd < 0)error_print("socket");struct sockaddr_in servaddr;bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;/*IPV4*/servaddr.sin_port = 1234;servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");/*使用本地环回地址做测试*//*inet_aton("127.0.0.1",&servaddr.sin_addr);//与inet_addr函数作用相同*//*setsockopt确保服务器不用等待TIME_WAIT状态结束就可以重启服务器,继续使用原来的端口号*/int on = 1;if( setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0)error_print("setsockopt");/*绑定本地Socket地址*/if(bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)error_print("bind");/*监听连接*/if(listen(sockfd, SOMAXCONN) < 0)error_print("listen");struct sockaddr_in peeraddr;/*存储连接成功的客户端Socket信息*/socklen_t peerlen = sizeof(peeraddr);int conn;/*接收监听队列第一个完成连接的请求*/if((conn = accept(sockfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0)error_print("accept");pid_t pid;pid = fork();/*创建一个新的子进程*/if(pid == -1){error_print("fork");}if(pid == 0){/*子进程中用来向客户端发送数据*/signal(SIGUSR1,quit_tranmission);/*回调函数处理通信中断*/char send_buf[MAX_BUF_LEN]={0};/*如果客户端Ctrl+C结束通信进程,fgets获取的就是NULL,否则就进入循环正常发送数据*/while(fgets(send_buf, sizeof(send_buf), stdin) != NULL){write(conn,send_buf,strlen(send_buf));bzero(send_buf,strlen(send_buf));/*发送完成清空发送缓冲区*/}exit(EXIT_SUCCESS);/*成功退出子进程*/}else{char recv_buf[MAX_BUF_LEN]={0};while(1){bzero(recv_buf,strlen(recv_buf));int ret = read(conn, recv_buf, sizeof(recv_buf));/*读取conn连接发送过来的数据*/if(ret < 0)error_print("read");else if(ret == 0){printf("client is close!\n");break;//父进程收到服务器端退出的信息(服务器Ctrl+C结束通信进程,read函数返回值为0,退出循环)}fputs(recv_buf,stdout);}kill(pid,SIGUSR1);/*父进程结束,也要向子进程发出一个信号告诉子进程终止接收,否则子进程会一直等待输入*/}close(conn);close(sockfd);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

收到对端结束信息(NULL信息)的进程要向等待发送的进程发送一个结束通信的信号,回调函数处理使得等待输入的进程结束,否则该进程会一直等待,直到有输入(但此时的输入已经没有意义,所以应提早结束,而不是一直等待)。

三、测试结果:

这个不厚道的服务器结束了通信:

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

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

    转自:http://blog.csdn.net/apollon_krj/article/details/53398448#0-tsina-1-64987-397232819ff9a47a7b7e80a ...

  2. Linux下Socket编程之TCP应用

    现在,我们用前面所构建的socket类,重新设计<Linux下Socket编程之TCP Server端>中echo的服务器,然后设计客户端程序. echo服务器的工作原理很简单: 1.接收 ...

  3. Linux下Socket编程之TCP Server端

    一.建模 绝大部分关于socket编程的教程总是从socket的概念开始讲起的.要知道,socket的初衷是个庞大的体系,TCP/IP只是这个庞大体系下一个很小的子集,而我们真正能用上的更是这个子集中 ...

  4. Linux C socket 编程之TCP

    本文主要是,简单实现tcp连接的两个程序.本文编写,假设读者有socket 编程思想.熟悉C编程. 服务端: #include <stdio.h> #include <stdlib. ...

  5. Linux下Socket编程之TCP原理

    一.Socket异常信息 之所以把对异常信息的介绍放到原理之前讲,是因为由于socket本身的复杂性,导致了产生各种异常的复杂性.我们应该时刻铭记的是,sokcet本身属于系统(OS),是系统对TCP ...

  6. Linux下socket编程之UDP简单实现

    本文实现一个简单的UDP小例子,来说明Linux下socket编程之UDP的简单实现.本文主要包括三个部分:服务器端的实现,客服端的实现和通信测试.实现的功能:客服端发送一条消息给服务器端,服务器端把 ...

  7. windows Socket编程之TCP服务端与客户端

    在前面的文章中有一篇讲到了命名管道通信,它是创建一根管道来进行进程之间或网络之间通信的.但是它有些缺陷,比如说效率较低等.而从这篇文章开始将介绍socket编程.socket是通过TCP,UDP,IP ...

  8. php soecket服务器搭建_Linux系统编程(32)—— socket编程之TCP服务器与客户端

    TCP协议的客户端/服务器程序的一般流程 aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgQAAAJmCAYAAAAjPpjHAAAgAElEQVR4A ...

  9. linux socket编程之TCP与UDP

    转:http://blog.csdn.net/gaoxin1076/article/details/7262482 TCP/IP协议叫做传输控制/网际协议,又叫网络通信协议 TCP/IP虽然叫传输控制 ...

最新文章

  1. 纯CSS实现蓝色圆角下拉菜单
  2. Javascript调用OCX控件
  3. 2021-01-07 matlab数值分析 线性方程组的迭代解法 高斯-赛德尔迭代法
  4. 2020/Province_C_C++_A/A/门牌制作
  5. HDU - 4635 Strongly connected(强连通缩点+数学+思维)
  6. mac中NSScrollView自定义滑动条NSScroller
  7. 开发 自我介绍_对于开发者来说,自我是敌人
  8. Job for slapd.service failed because the control process exited with error code. See systemctl stat
  9. iOS内存管理部分内容
  10. GAMES101 Transformation Cont.
  11. VS QT进行相机镜头控制软件二次开发
  12. 基于 Elasticsearch 的站内搜索引擎实战
  13. 华为系统wifi服务器失败是怎么回事儿,wifi 用云服务器异常
  14. excel 计算机职称,职称计算机2017年Excel知识点:工作表的编辑
  15. 在 Oracle Enterprise Linux 和 iSCSI 上构建您自己的 Oracle RAC 集群(续)
  16. hook之useRoducer
  17. 中文汉字注音,汉字转拼音,支持图片识别文字,支持结果转图下载,附上小程序核心源码
  18. php获取腾讯视频信息,云水日记-PHP实现腾讯视频解析源码
  19. 关于emjoy表情在android5.x以上系统触发jni错误的修改(基于cocos2dx2.1.5修改)
  20. pygame模块参数汇总

热门文章

  1. CentOS7 安装NodeJS
  2. css cursor url用法格式详解
  3. 解析Json需要设置Mime
  4. 开窍小老虎,一步一个脚印之 初识汇编(一)
  5. 步骤菜单使用css3实现
  6. C++中的指针与引用(转)
  7. 转:JNI jstring与c++字符串类型转换函数
  8. Rose与PowerDesigner:两款建模工具对比分析比较
  9. php原生sql语法,thinkphp执行原生SQL语句的实现方法
  10. linux人脸识别视频推流,RTMP推流协议视频智能分析/人脸识别/直播点播平台EasyDSS接口调用注意事项介绍...