对于socket 通信,大家很多都用的单线程通信。同时只能监听一个端口,只能响应一个服务,select的方式可以解决多个socket 被连接的问题。一次可以分配多个资源,只要一个连接便可以进行通信。在网络已经有很多的select 的例子。不过很多例子没有真正体现到select的精妙之处。此函数主要是对多个文件描述符进行监听,直到某一个或者多个被连接。

  首先我们介绍fd_set这个结构:

  fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor),即文件句柄,这可以是我们所说的普通意义的文件,当然Unix下任何设备、管道、FIFO等都是文件形式,全部包括在内,所以毫无疑问一个socket就是一个文件,socket句柄就是一个文件描述符。fd_set集合可以通过一些宏由人为来操作,比如清空集合 FD_ZERO(fd_set *),将一个给定的文件描述符加入集合之中FD_SET(int ,fd_set *),将一个给定的文件描述符从集合中删除FD_CLR(int ,fd_set*)。

  在select使用这个结构之前,我们需要调用FD_SET,设置对应socket的标志位,网络生很多的例子错误就在此,这里必须要绑定多个socket才是真正体会了select的多fd监听。而且不能随意指定,比如FD_SET(0,XX),这样的fd实际已经被系统占用了。在select成功返回后检查集合中指定的文件描述符是否可以读写FD_ISSET(int ,fd_set* )。进而根据对应的fd进行读写操作。不多说,直接粘贴代码:

 #include <iostream>
#include <sys/times.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
using namespace std; #define max(a,b) ((a)>(b)?(a):(b)) static int listen_socket(int port)
{
sockaddr_in s_in;
int s;
int yes;
if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -)
{
cout<<"socket error"<<strerror(errno)<<endl;
return -;
}
yes = ;
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&yes,sizeof(yes)) == -)
{
cout<<"setsockopt error"<<strerror(errno)<<endl;
close(s);
return -;
} memset(&s_in, , sizeof(s_in));
s_in.sin_port = htons(port);
s_in.sin_family = AF_INET;
s_in.sin_addr.s_addr = inet_addr("127.0.0.1");
if(bind(s,(sockaddr*)&s_in, sizeof(s_in)) == -)
{
cout<<"bind error"<<strerror(errno)<<endl;
close(s);
return -;
}
listen(s,);
cout<<"socket -> setsockopt-> bind->listen"<<port<<endl;
return s;
} int main(int argi, char* args[])
{
int h, h1;
int fd1 = -, fd2 = -; if(argi != )
{
cout<<"Usage server listen_port1 listen_port2"<<endl;
exit(-); } int listen_port1 = atoi(args[]);
fd1 = listen_socket(listen_port1);
int listen_port2 = atoi(args[]);
fd2 = listen_socket(listen_port2);//这里是建立了两个socket:cyjwdm0503
if(fd1==- || fd2==-)
{
exit(-);
}
for(;;)
{
int r, nfds = ;
fd_set rfd,sfd; FD_ZERO(&rfd);
FD_ZERO(&sfd);
FD_SET(fd1, &rfd);//绑定对应的两个socket
FD_SET(fd2,&rfd); int maxnum = max(, fd1);
maxnum = max(fd2, maxnum);
cout<<"maxnum:"<<maxnum<<"\t"<<h<<endl; r = select(maxnum+, &rfd, &sfd, NULL, NULL);//select的参赛为rfd,最大值为socket+1
if( r == - && errno == EINTR)
{
cout<<strerror(errno)<<endl;
continue;
}
if( r == -)
{
cout<<"select error"<<strerror(errno)<<"\t"<<errno<<endl;
return -;
} // for(int index=1; index<=5; index++)
{
int select_fd = ;
if(FD_ISSET(fd1,&rfd))
select_fd = fd1;
else if(FD_ISSET(fd2,&rfd))
select_fd = fd2;
else
select_fd = -;
if(select_fd != -)
{
cout<<"select retrun fd"<<select_fd<<endl;
sockaddr_in accept_addr;
socklen_t le = sizeof(accept_addr);//注意在accept时候的sockaddr_in 长度要初始化
int acc = accept(ddd, (sockaddr*)&accept_addr, &le); cout<<"accept port"<<accept_addr.sin_port<<endl; if(- == acc)
{
cout<<"accept error"<<strerror(errno)<<endl;
return -;
}
char buffer[] = "";
int ret = recv(acc, buffer, sizeof(buffer),);
if(ret <= )
{
cout<<"client close:"<<strerror(errno)<<"\t"<<errno<<endl;
}
else
{
cout<<"buffer from client:"<<buffer<<endl;
} }
else
cout<<"select_fd = -1"<<endl;
}
cout<<"for over"<<endl; }
}

  select 在监听对应的socket的rfd集合时,如果有对应的客户端链接两个socket之中的某一个,select 边能够返回对应的信息,然后调用FD_ISSET,获取对应被链接的socket ,进行accept ,收发信息。

  下面为客户端的示例代码:

 #include <iostream>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netdb.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h> using namespace std; int main(int argi, char* args[])
{
if( argi<)
{
cout<<args[]<<"\t"<<"host port msg..."<<endl;
return -;
}
sockaddr_in s_in;
memset(&s_in, , sizeof(s_in));
s_in.sin_family = AF_INET;
s_in.sin_addr.s_addr = inet_addr("127.0.0.1");//(args[1]);
s_in.sin_port = htons(atoi(args[])); cout<<"port"<<"\t"<<args[]<<endl;
int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if( s == -)
{
cout<<"socket error"<<strerror(errno)<<endl;
} socklen_t length = sizeof(sockaddr_in);
if(- == connect(s, (sockaddr*)&s_in, length))
{
cout<<"connect error"<<strerror(errno)<<endl;
close(s);
return -;
}
for(;;)
{
char buffer[];
if(strcmp(args[2],"q")==)
break;
ssize_t size = send(s, buffer, strlen(buffer),);
cout<<"socket num:"<<s<<endl;
   sprintf(buffer,"%s%s",args[2],"www.cnblogs.com/cyjwdm0503");
size = send(s,args[],strlen(args[]),);
if( - == size)
{
cout<<"write error"<<strerror(errno)<<endl;
close(s);
return -;
}
}
close(s); }

虽然这里是以Linux为示例,Windows和这个也类似的,转载请注明来源地址http://www.cnblogs.com/cyjwdm0503

上面如果有问题,请最好留言给我,大家交流。

socket 通信之select的更多相关文章

  1. socket通信中select函数的使用和解释

    select函数的作用: select()在SOCKET编程中还是比较重要的,可是对于初学SOCKET的人来说都不太爱用select()写程序,他们只是习惯写诸如 conncet().accept() ...

  2. Linux Network IO Model、Socket IO Model - select、poll、epoll

    目录 . 引言 . IO机制简介 . 阻塞式IO模型(blocking IO model) . 非阻塞式IO模型(noblocking IO model) . IO复用式IO模型(IO multipl ...

  3. JAVA基础知识之网络编程——-基于NIO的非阻塞Socket通信

    阻塞IO与非阻塞IO 通常情况下的Socket都是阻塞式的, 程序的输入输出都会让当前线程进入阻塞状态, 因此服务器需要为每一个客户端都创建一个线程. 从JAVA1.4开始引入了NIO API, NI ...

  4. Socket通信中AF_INET 和 AF_UNIX域的区别

    转载:http://blog.csdn.net/sandware/article/details/40923491 1.  AF_INET域socket通信过程 典型的TCP/IP四层模型的通信过程. ...

  5. 基于NIO的Socket通信

    一.NIO模式的基本原理: 服务端: 首先,服务端打开一个通道(ServerSocketChannel),并向通道中注册一个通道调度器(Selector):然后向通道调度器注册感兴趣的事件Select ...

  6. AF_INET域与AF_UNIX域socket通信原理对比【转】

    转自:https://www.cnblogs.com/lfxiao/p/9672797.html 1.  AF_INET域socket通信过程 典型的TCP/IP四层模型的通信过程. 发送方.接收方依 ...

  7. Linux 下socket通信终极指南(附TCP、UDP完整代码)

    linux下用socket通信,有TCP.UDP两种协议,网上的很多教程把两个混在了一起,或者只讲其中一种.现在我把自己这两天研究的成果汇总下来,写了一个完整的,适合初学者参考,也方便自己以后查阅. ...

  8. AF_INET域与AF_UNIX域socket通信原理对比

    原文 1.  AF_INET域socket通信过程 典型的TCP/IP四层模型的通信过程. 发送方.接收方依赖IP:Port来标识,即将本地的socket绑定到对应的IP端口上,发送数据时,指定对方的 ...

  9. socket编程之select相关

    FD_ZERO,FD_ISSET这些都是套节字结合操作宏 看看MSDN上的select函数, 这是在select   io   模型中的核心,用来管理套节字IO的,避免出现无辜锁定. int   se ...

随机推荐

  1. 关于ASP.NET 中站点地图sitemap 的使用

    在ASP.NET  MVC 如此火热的时期,我竟然不适时宜的谈起ASP.NET ,恐怕会引来一阵嘲笑.最为无趣的是,讲解的竟然还是其中的一个控件.oh~~  my god!my out! ^_^ Si ...

  2. Sql Server相关的性能计数器

    OS Memory and Paging 性能计数器: 1.Memory\Availability Mbytes   未使用的物理内存(非页面文件),通常情况下它应该大于100MB 2.Memory\ ...

  3. 005 列表以及append,extend方法

    定义一个列表: number=[,'changhao','常浩',5.2] 往这个列表里面添加单一新值(类型无限制),需要使用append方法. 例如: number.append() number. ...

  4. Spring:启动项目时加载数据库数据(总结)

    在项目中需要启动程序时,要将数据库的用户信息表加载到内存中,找到一下几种方式. 1.实现ApplicationListener接口,重写onApplicationEvent方法,可以在项目启动的时候执 ...

  5. 安装完CentOS 7 Minimal之后,从头打造桌面工作环境

    安装完CentOS 7 Minimal之后,从头打造桌面工作环境 U盘装CentOS 7 DVD版不能引导的解决办法 更改root密码 SSH登录 增加除root之外的常规用户 装完CentOS 7之 ...

  6. Objextive-C几道小题目笔记

    //掷骰子题,掷骰子100次,输出每个号出现的次数 void one() { for (int i=1; i<=100; i++) { int a = arc4random() % 6 +1; ...

  7. nginx上传模块—nginx upload module-

    一. nginx upload module原理 官方文档: http://www.grid.net.ru/nginx/upload.en.html Nginx upload module通过ngin ...

  8. Filter与Servlet的区别和联系

    Filter Servlet 接口 实现Filter接口 实现Servlet接口 使用步骤 1.创建类,继承接口 2.实现方法 init() doFilter() destroy() 3.配置WEB- ...

  9. SDK Manager 报错:Connection timed out: connect

    安装Eclipse的安卓开发环境的时候,安装sdk时报错,出现: 解决办法: 1.选择左上角的Tools 2.选择Options,勾选下面红色框的东西 3. 4.重新重启一下sdk manager即可

  10. Delphi调用安装驱动sys的单元

    unit SysDriver; interface uses windows, winsvc; // jwawinsvc; Type TSysDriver = class(TObject) priva ...