基于Linux的Socket编程之TCP全双工Server-Client聊天程序
转载: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聊天程序相关推荐
- 基于Linux的SOCKET编程之TCP半双工Client-Server聊天程序
转自:http://blog.csdn.net/apollon_krj/article/details/53398448#0-tsina-1-64987-397232819ff9a47a7b7e80a ...
- Linux下Socket编程之TCP应用
现在,我们用前面所构建的socket类,重新设计<Linux下Socket编程之TCP Server端>中echo的服务器,然后设计客户端程序. echo服务器的工作原理很简单: 1.接收 ...
- Linux下Socket编程之TCP Server端
一.建模 绝大部分关于socket编程的教程总是从socket的概念开始讲起的.要知道,socket的初衷是个庞大的体系,TCP/IP只是这个庞大体系下一个很小的子集,而我们真正能用上的更是这个子集中 ...
- Linux C socket 编程之TCP
本文主要是,简单实现tcp连接的两个程序.本文编写,假设读者有socket 编程思想.熟悉C编程. 服务端: #include <stdio.h> #include <stdlib. ...
- Linux下Socket编程之TCP原理
一.Socket异常信息 之所以把对异常信息的介绍放到原理之前讲,是因为由于socket本身的复杂性,导致了产生各种异常的复杂性.我们应该时刻铭记的是,sokcet本身属于系统(OS),是系统对TCP ...
- Linux下socket编程之UDP简单实现
本文实现一个简单的UDP小例子,来说明Linux下socket编程之UDP的简单实现.本文主要包括三个部分:服务器端的实现,客服端的实现和通信测试.实现的功能:客服端发送一条消息给服务器端,服务器端把 ...
- windows Socket编程之TCP服务端与客户端
在前面的文章中有一篇讲到了命名管道通信,它是创建一根管道来进行进程之间或网络之间通信的.但是它有些缺陷,比如说效率较低等.而从这篇文章开始将介绍socket编程.socket是通过TCP,UDP,IP ...
- php soecket服务器搭建_Linux系统编程(32)—— socket编程之TCP服务器与客户端
TCP协议的客户端/服务器程序的一般流程 aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgQAAAJmCAYAAAAjPpjHAAAgAElEQVR4A ...
- linux socket编程之TCP与UDP
转:http://blog.csdn.net/gaoxin1076/article/details/7262482 TCP/IP协议叫做传输控制/网际协议,又叫网络通信协议 TCP/IP虽然叫传输控制 ...
最新文章
- 纯CSS实现蓝色圆角下拉菜单
- Javascript调用OCX控件
- 2021-01-07 matlab数值分析 线性方程组的迭代解法 高斯-赛德尔迭代法
- 2020/Province_C_C++_A/A/门牌制作
- HDU - 4635 Strongly connected(强连通缩点+数学+思维)
- mac中NSScrollView自定义滑动条NSScroller
- 开发 自我介绍_对于开发者来说,自我是敌人
- Job for slapd.service failed because the control process exited with error code. See systemctl stat
- iOS内存管理部分内容
- GAMES101 Transformation Cont.
- VS QT进行相机镜头控制软件二次开发
- 基于 Elasticsearch 的站内搜索引擎实战
- 华为系统wifi服务器失败是怎么回事儿,wifi 用云服务器异常
- excel 计算机职称,职称计算机2017年Excel知识点:工作表的编辑
- 在 Oracle Enterprise Linux 和 iSCSI 上构建您自己的 Oracle RAC 集群(续)
- hook之useRoducer
- 中文汉字注音,汉字转拼音,支持图片识别文字,支持结果转图下载,附上小程序核心源码
- php获取腾讯视频信息,云水日记-PHP实现腾讯视频解析源码
- 关于emjoy表情在android5.x以上系统触发jni错误的修改(基于cocos2dx2.1.5修改)
- pygame模块参数汇总
热门文章
- CentOS7 安装NodeJS
- css cursor url用法格式详解
- 解析Json需要设置Mime
- 开窍小老虎,一步一个脚印之 初识汇编(一)
- 步骤菜单使用css3实现
- C++中的指针与引用(转)
- 转:JNI jstring与c++字符串类型转换函数
- Rose与PowerDesigner:两款建模工具对比分析比较
- php原生sql语法,thinkphp执行原生SQL语句的实现方法
- linux人脸识别视频推流,RTMP推流协议视频智能分析/人脸识别/直播点播平台EasyDSS接口调用注意事项介绍...