对于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. 《JavaScript 闯关记》之函数

    函数是一段代码,它只定义一次,但可以被执行或调用任意次.在 JavaScript 里,函数即对象,程序可以随意操控它们.比如,可以把函数赋值给变量,或者作为参数传递给其他函数,也可以给它们设置属性,甚 ...

  2. wamp安装

    下载之后双击文件进行安装选择:I accept the agreement ,点击Next. 一直单击NEXT 安装完成后运行wamp,在桌面右下角即会出现wamp的图标,图标最初是红色的,然后变为橙 ...

  3. js 倒计时 已过去时间

    页面中的代码: <strong id="timer" datatime="2012-12-09 10:20:30"></strong> ...

  4. Entity Framework 6 Code First创建

    基本上我是DB先设计好的,所以就按现存在的table去写程式. 1.Web.config里配置Db连接字串,Connection String Name为DefaultConnection <c ...

  5. <转>golang 并发性能数据

    1.管道chan吞吐极限10,000,000,单次Put,Get耗时大约100ns/op,无论是采用单Go程,还是多Go程并发(并发数:100, 10000, 100000),耗时均没有变化,Go内核 ...

  6. JAVA多线程下,获取递增的序列号

    场景描述: 1,目前我们的系统可以简单归纳成MVC的架构模式 2,每个前端的请求过来,都会在C层开启事务,最后处理结束后,也在在C层关闭事务(实际是在C层的底层统一做了事务的开启和提交):      ...

  7. c#中override重写和new隐藏

    最近学习c#,昨晚看书看到多态.由于个人本身是从事java开发,于是拿来做对比便是自然的. 进入主题吧. c#中,子类要重写基类的方法,必须要基类声明中带有virtual关键字方法或者带有abstra ...

  8. Ubuntu 14.04卸载安装失败的Mysql数据库,以及重新安装配置

    一.删除原来Mysql 1.删除mysql的数据文件 sudo rm /var/lib/mysql/ -R 2.删除mqsql的配置文件 sudo rm /etc/mysql/ -R 3.自动卸载my ...

  9. Max Sum(hd P1003)

    Problem Description Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max sum ...

  10. C语言学习second--C语言基础学习

    1.标准C语言 C语言诞生于20世纪70年代,年龄比我们自己还要大,期间产生了很多标准,但是各种编译器对标准的支持不尽相同. ANSI C是使用的最广泛的一个标准,也是第一个正式标准,被称为“标准C语 ...