对于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. css之background的cover和contain的缩放背景图

    对于这两个属性,官网是这样解释的: contain 此时会保持图像的纵横比并将图像缩放成将适合背景定位区域的最大大小. 等比例缩放图象到垂直或者水平其中一项填满区域. cover 此时会保持图像的纵横 ...

  2. Android Studio自定义注释模板及生成JavaDoc

    刚开始学习Android,使用了Android Studio IDE.为了将来生产JavaDoc,学习一下如何自定义注释模板. . 自定义注释模板 1. 通过 File –>Settings 或 ...

  3. A child container failed during start 解决方案

    症状:A child container failed during start 适用问题描述:tomcat挂空可以正常运行,载入项目时挂掉. 相关操作:之前为了省事,由于两个servlet功能类似所 ...

  4. 枚举 enum

    typedef enum : NSUInteger { <#MyEnumValueA#>, <#MyEnumValueB#>, <#MyEnumValueC#>, ...

  5. 【转】GATT Profile 简介

    网上关于讲解 BLE 的内容比较少,看到这篇文章写的非常详细 Introduction to Bluetooth Low Energy,作为 BLE 的入门时介绍是非常合适的.本文主要翻译了一下这篇文 ...

  6. spring 配置和实例

    Spring 是一个开源框架.Spring 为简化企业级应用开发而生. 使用 Spring 可以使简单的 JavaBean 实现以前只有 EJB 才能实现的功能.Spring 是一个 IOC(DI) ...

  7. Spring-----9、容器中bean的生命周期

    转载自:http://blog.csdn.net/hekewangzi/article/details/45648771

  8. C++在数组元素个数未知情况下声明数组

    我们都从书上学习的方法,定义一个数组需要数组名.类型以及数组元素个数,一般定义必须明确元素的个数,否则无法通过编译. 1. int a[]; 2. int n; int a[n]; 就想上面这两种情况 ...

  9. java教程

    http://www.xfonlineclass.com/ http://java.itcast.cn/ http://www.xasxt.com/index.php/list/161 [UI]htt ...

  10. Android Adapter代码片

    /** * Adapter for grid of coupons. */ private static class CouponAdapter extends BaseAdapter { priva ...