一.I/O复用

在《TCP套接字编程》的同步聊天程序中,我们看到TCP客户同时处理两个输入:标准输入和TCP套接字。考虑在客户阻塞于标准输入fgets调用时,服务器进程被杀死,服务器TCP虽然会给客户TCP发送一个FIN,但是客户客户进程正阻塞于标准输入读入过程,它将看不到这个EOF,直到从套接字读时为止。这样的进程需要一种预先告知内核的能力,使得内核一旦发现内核指定的一个或多个I/O条件就绪,它就通知进程。这个能力就称之为I/O复用,由select和poll这两个函数支持。

I/O复用通常应用在下列场合:

  • 当客户处理多个应用场合时(交互式输入和网络套接字);
  • 一个客户同时处理多个套接字;
  • 一个TCP服务器既要处理监听套接字,又要处理已连接套接字;
  • 一个服务器既要处理TCP,又要处理UDP;
  • 一个服务器要处理多个服务或者多个协议。

二.select函数

select函数允许进程指示内核等待多个事件中的任意一个发生,并只在有一个或多个事件发生或经历一段指定时间后才唤醒它。

#include<sys/select.h>
#include<sys/time.h>
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);

返回:若有就绪描述符则返回其数目,若超时返回0,若出错返回1。

头文件<sys/select.h>中定义的FD——SETSIZE常值是数据类型fd_set中的描述符总数,其值通常是。

maxfdp1参数指定带测试的描述符个数,它的值是带测试最大描述符加1,描述符0,1,2,...,maxfdp1-1均将被测试。

中间三个参数readset,writeset和exceptset指定我们要让内核测试读/写和异常条件的描述符。

void FD_ZERO(fd_set *fdset);         //clear all bits in fdset
void FD_SET(int fd,fd_set *fdset); //turn on the bit for fd in fdset
void FD_CLR(int fd,fd-set *fdset); //turn off the bit for fd in fdset
int FD_ISSET(int fd,fd_set *fdset); //is the bit for fd on in fdset?

其中,描述符集的初始化非常重要。

参数timeout告知内核等待所指定描述符中的任何一个就绪可花多少时间,timeval结构用于指定这段时间的秒数和微妙数。

struct timeval
{
long tv_sec; //second
long tv_usec; //microsecond
};

这个参数有三种可能:

  • 永远等待下去:仅在有一个描述符准备好I/O时才返回,为此,把该参数设为空;
  • 等待一段固定时间:在有一个描述符准备好I/O时返回,但是不超过timeval所指定时间;
  • 根本不等待:检查描述符后立即返回,这个称为轮询(polling),为此,秒数和微妙数必须为0。

三.异步聊天程序

写一个TCP异步聊天程序来加深理解。

服务器代码:

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include <unistd.h>
#include<time.h> #define MAXSIZE 1024
#define PORT 8080
#define BACKLOG 10 int main(int argc,char **argv)
{
int listenfd,connfd;
struct sockaddr_in servaddr,cliaddr;
socklen_t len;
char message[MAXSIZE]; fd_set rfds;
// struct timeval tv;
int retval,maxfd=-1; if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket");
exit(1);
}
else printf("socket create success!\n"); bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(PORT); if((bind(listenfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr)))==-1)
{
perror("bind");
exit(1);
}
else printf("bind success!\n"); if(listen(listenfd,BACKLOG)==-1)
{
perror("listen");
exit(1);
}
else printf("sever is listening!\n"); for( ; ; )
{
printf("等待连接...\n");
len=sizeof(struct sockaddr);
if((connfd=accept(listenfd,(struct sockaddr*)&cliaddr,&len))==-1)
{
perror("accept");
exit(1);
}
else printf("客户端:%s: %d\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
printf("开始聊天!\n");
for( ; ; )
{
FD_ZERO(&rfds);
FD_SET(0,&rfds);
maxfd=0;
FD_SET(connfd,&rfds);
if(connfd>maxfd) maxfd=connfd;
retval=select(maxfd+1,&rfds,NULL,NULL,NULL);
if(retval==-1)
{
printf("select出错!%s",strerror(errno));
break;
}
else if(retval==0)
{
printf("等待对方输入...\n");
continue;
}
else
{
if(FD_ISSET(0,&rfds))
{
bzero(message,MAXSIZE);
printf("输入:");
fgets(message,MAXSIZE,stdin); if(!strncasecmp(message, "quit", 4))
{
printf("终止聊天!\n");
break;
}
else len=send(connfd,message,strlen(message),0);
if(len<0)
{
printf("发送失败");
break;
}
}
if(FD_ISSET(connfd,&rfds))
{
bzero(message,MAXSIZE);
len=recv(connfd,message,MAXSIZE,0);
if(len>0) printf("客户端:%s",message);
else
{
if(len<0) printf("接受消息失败!\n");
else printf("客户端不在线!\n");
break;
}
}
}
}
close(connfd);
printf("是否退出服务器[Y/N]:");
bzero(message,MAXSIZE);
fgets(message,MAXSIZE,stdin);
if(!strncasecmp(message, "Y", 1))
{
printf("服务器已退出!\n");
break;
}
}
close(listenfd);
return 0;
}

客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h> #define MAXSIZE 1024
#define PORT 8080 int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
socklen_t len;
fd_set rfds;
// struct timeval tv;
int retval,maxfd=-1; char message[MAXSIZE]; if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("socket");
exit(1);
}
else printf("socket create success!\n"); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT); inet_aton(argv[1],&servaddr.sin_addr); if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr))==-1)
{
perror("connect");
exit(1);
}
else printf("conncet success!\n"); for( ; ; )
{
FD_ZERO(&rfds);
FD_SET(0,&rfds);
maxfd=0;
FD_SET(sockfd,&rfds);
if(sockfd>maxfd) maxfd=sockfd;
retval=select(maxfd+1,&rfds,NULL,NULL,NULL);
if(retval==-1)
{
printf("select出错!%s",strerror(errno));
break;
}
else if(retval==0)
{
printf("等待对方输入...\n");
continue;
}
else
{
if(FD_ISSET(sockfd,&rfds))
{
bzero(message,MAXSIZE);
len=recv(sockfd,message,MAXSIZE,0);
if(len>0)
printf("服务器:%s",message);
else
{
if(len<0) printf("接受消息失败!\n");
else printf("服务器已退出!\n");
break;
}
}
if(FD_ISSET(0,&rfds))
{
bzero(message,MAXSIZE);
printf("输入:");
fgets(message,MAXSIZE,stdin); if(!strncasecmp(message, "quit", 4))
{
printf("client 请求终止聊天!\n");
break;
}
else len = send(sockfd,message,strlen(message),0);
if(len<0)
{
printf("消息发送失败!\n");
break;
}
}
}
}
close(sockfd);
return 0;
}

编译:

gcc -Wall server.c -o server
gcc -Wall client.c -o client

服务器运行结果:

./server
socket create success!
bind success!
sever is listening!
等待连接...
客户端:127.0.0.1: 50235
开始聊天!
客户端:
客户端:你好啊,服务器!
客户端:我是客户。 输入:你好啊,客户端!
输入:客户端:
客户端:Byebye!
客户端不在线!
是否退出服务器[Y/N]:Y
服务器已退出!

客户端运行结果:

./client 127.0.0.1
socket create success!
conncet success! 输入:你好啊,服务器!
输入:我是客户端。
输入:服务器:
服务器:你好啊,客户端! 输入:Byebye!
输入:quit
输入:client 请求终止聊天!

I/O复用:异步聊天的更多相关文章

  1. C# Socket异步聊天例子

    最近在配合游戏服务器端搞一个客户端通信,客户端是unity搞的,理所当然就高C#了,上手之前先看了一下C# Socket通信这一块,基本不考虑同步方式,而异步方式,微软也提供了两套API,一套是Beg ...

  2. Django项目--web聊天室

    需求 做一个web聊天室,主要练习前端ajax与后台的交互: 一对一聊天和群组聊天 添加用户为好友 搜索并添加群组 管理员可以审批用户加群请求,群管理员可以有多个,群管理员可以删除,添加禁言群友 与聊 ...

  3. WebSocket聊天室demo

    根据Socket异步聊天室修改成WebSocket聊天室 WebSocket特别的地方是 握手和消息内容的编码.解码(添加了ServerHelper协助处理) ServerHelper: using ...

  4. nginx、swoole高并发原理初探

    阅前热身 为了更加形象的说明同步异步.阻塞非阻塞,我们以小明去买奶茶为例. 同步与异步 同步与异步的重点在消息通知的方式上,也就是调用结果通知的方式. 同步:当一个同步调用发出去后,调用者要一直等待调 ...

  5. PHP并发IO编程之路

    并发IO问题一直是服务器端编程中的技术难题,从最早的同步阻塞直接Fork进程,到Worker进程池/线程池,到现在的异步IO.协程.PHP程序员因为有强大的LAMP框架,对这类底层方面的知识知之甚少, ...

  6. [转]PHP并发IO编程之路(深度长文)

    原文:https://www.imooc.com/article/8449 -------------------------------------------------------------- ...

  7. Linux 的 Socket IO 模型

    前言 之前有看到用很幽默的方式讲解Windows的socket IO模型,借用这个故事,讲解下linux的socket IO模型: 老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系. 他 ...

  8. PHP如何解决网站大流量与高并发的问题(四)

    动态语言的并发处理 相关概念 什么是进程.线程.协程 什么是多进程.多线程 同步阻塞模型 异步非阻塞模型 php并发编程实践 什么是进程.线程.协程 进程 进程是一个执行中的程序 进程的三态模型:多道 ...

  9. Python基础+Pythonweb+Python扩展+Python选修四大专题 超强麦子学院Python35G视频教程

    [保持在百度网盘中的, 可以在观看,嘿嘿 内容有点多,要想下载, 回复后就可以查看下载地址,资源收集不易,请好好珍惜] 下载地址:http://www.fu83.cc/ 感觉文章好,可以小手一抖 -- ...

随机推荐

  1. hdu 1042 N!

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1042 N! Description Given an integer N(0 ≤ N ≤ 10000) ...

  2. Android开发遇到的异常及解决办法

    Android开发遇到的错误及解决方法1. Unable to resolve target 'android-7' 解决方案: 修改工程目录下的default.properties文件里的内容tar ...

  3. JavaScript高级程序设计之作用域链

    JavaScript只有函数作用域:每个函数都有个作用域链直达window对象. 变量的查找由内而外层层查找,找到即止. 同时不仅可以查找使用,甚至可以改变外部变量. var color = &quo ...

  4. 整理了一下 jQuery 的原型关系图,理解起来更加方便一些。

    图例:黄色的为对象,蓝色的为函数.

  5. 以Lockbits的方式访问bitmap

    转载自:http://www.cnblogs.com/xiashengwang/p/4225848.html 2015-01-15 11:38 by xiashengwang, 585 阅读, 0 评 ...

  6. Graceful degradation versus progressive enhancement

    http://ued.taobao.org/blog/2008/10/understanding-progressiveen-hancement-chs-translation/ http://www ...

  7. 20145120 《Java程序设计》第6周学习总结

    20145120 <Java程序设计>第6周学习总结 教材学习内容总结 java.io.InputStream.java.io.OutputStream实例分别作为输入.输出串流的代表对象 ...

  8. vmware workstation 10.0

    2013.9.3 vmware workstation 10.0 build 1295980新增功能– 可以将windows 8.1物理pc转变为虚拟机:unity模式增强,与windows 8.1 ...

  9. Combox 实现百度收索框效果

    标题中所谓百度收缩框效果,就是在输入数据的时候,自动提示,来张图就明白了: 用Combox来实现这个功能只是需要设置三个A开头的属性就OK了:AutoCompleteSource.AutoComple ...

  10. poi实现Excel比较

    http://stackoverflow.com/questions/866346/easiest-way-to-compare-two-excel-files-in-java http://stac ...