socket 通信之select
对于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的更多相关文章
- socket通信中select函数的使用和解释
select函数的作用: select()在SOCKET编程中还是比较重要的,可是对于初学SOCKET的人来说都不太爱用select()写程序,他们只是习惯写诸如 conncet().accept() ...
- Linux Network IO Model、Socket IO Model - select、poll、epoll
目录 . 引言 . IO机制简介 . 阻塞式IO模型(blocking IO model) . 非阻塞式IO模型(noblocking IO model) . IO复用式IO模型(IO multipl ...
- JAVA基础知识之网络编程——-基于NIO的非阻塞Socket通信
阻塞IO与非阻塞IO 通常情况下的Socket都是阻塞式的, 程序的输入输出都会让当前线程进入阻塞状态, 因此服务器需要为每一个客户端都创建一个线程. 从JAVA1.4开始引入了NIO API, NI ...
- Socket通信中AF_INET 和 AF_UNIX域的区别
转载:http://blog.csdn.net/sandware/article/details/40923491 1. AF_INET域socket通信过程 典型的TCP/IP四层模型的通信过程. ...
- 基于NIO的Socket通信
一.NIO模式的基本原理: 服务端: 首先,服务端打开一个通道(ServerSocketChannel),并向通道中注册一个通道调度器(Selector):然后向通道调度器注册感兴趣的事件Select ...
- AF_INET域与AF_UNIX域socket通信原理对比【转】
转自:https://www.cnblogs.com/lfxiao/p/9672797.html 1. AF_INET域socket通信过程 典型的TCP/IP四层模型的通信过程. 发送方.接收方依 ...
- Linux 下socket通信终极指南(附TCP、UDP完整代码)
linux下用socket通信,有TCP.UDP两种协议,网上的很多教程把两个混在了一起,或者只讲其中一种.现在我把自己这两天研究的成果汇总下来,写了一个完整的,适合初学者参考,也方便自己以后查阅. ...
- AF_INET域与AF_UNIX域socket通信原理对比
原文 1. AF_INET域socket通信过程 典型的TCP/IP四层模型的通信过程. 发送方.接收方依赖IP:Port来标识,即将本地的socket绑定到对应的IP端口上,发送数据时,指定对方的 ...
- socket编程之select相关
FD_ZERO,FD_ISSET这些都是套节字结合操作宏 看看MSDN上的select函数, 这是在select io 模型中的核心,用来管理套节字IO的,避免出现无辜锁定. int se ...
随机推荐
- Android Studio Errors
1.The import org.apache.http.client; tip: cannot be resolved; resolve: add this line in build.gradle ...
- Android服务之AIDL
在android开发过程中,为了让其他的应用程序,也可以访问本应用程序的服务,android系统采用远程过程调用来实现.android通过接口来公开定义的服务.我们将能够夸进程访问的服务成为AIDL服 ...
- ios 添加多个target 管理 多个版本文件
1. 添加一个Target 这里是添加一个Test 项目 这里添加新的target Test与Release 也是同上的操作
- Android 使用BroadcastReceiver的几种方法
发送自定义广播 全局广播 发送标准广播 1.定义广播接收器.(在发送广播前,需要先定义一个广播接收器,不然发了也是白发) public class MyBroadcastReceiver extend ...
- Linux下,命令 wget 的使用
wget是一个从网络上自动下载文件的自由工具.它支持HTTP,HTTPS和FTP协议,可以使用HTTP代理. 所谓的自动下载是指,wget可以在用户退出系统的之后在后台执行.这意味这你可以登录系统, ...
- 接口(三)——JAVA的多重继承
一.接口的作用 ①.为了能够向上转型为多个基类型 ②.防止客户端程序员创建该类的对象——同抽象类 二.通过继承扩展接口 interface Monster{ void menace(); } inte ...
- ——转 token 介绍
学习Token Token是什么? Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Tok ...
- jquery multiselect控件
http://www.erichynds.com/blog/jquery-ui-multiselect-widget
- 一个简化的printf函数
<C和指针>第7章第5道编程题: 实现一个简化的printf函数,它能够处理%d.%f.%s 和 %c 格式码,根据ANSI标准的原则,其他格式码的行为是未定义的.你可以假定已经存在函数 ...
- SQL Server 备份的 8 种方法。
方法 1. 完整备份 方法 2. 差异备份 方法 3. 部分备份(备份数据库的read_write部分) 方法 4. 文件备份 方法 5. 文件组备份 方法 6. 只复制备份 方法 7. 日志备份 - ...