linux select编程
可以通过select写非阻塞io的程序,可以参考我的博客阻塞与非阻塞
函数介绍
select函数原型
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
参数:
第一个参数:int nfds是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1
第二个参数:fd_set *readfds 用来检查一组可读性的文件描述符。
第三个参数:fd_set *writefds 用来检查一组可写性的文件描述符。
第四个参数:fd_set *exceptfds 用来检查文件文件描述符是否异常
第五个参数:sreuct timeval *timeout是一个时间结构体,用来设置超时时间
timeout:最多等待时间,对阻塞操作则为NULL
时间体的结构:
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
返回值:
负值:select错误
正值:表示某些文件可读或可写
0:等待超时,没有可读写或错误的文件
select中常用的其他函数
void FD_CLR(int fd, fd_set *set);//用于在文件描述符集合中删除一个文件描述符。
int FD_ISSET(int fd, fd_set *set);//用于测试指定的文件描述符是否在该集合中。
void FD_SET(int fd, fd_set *set);//用于在文件描述符集合中增加一个新的文件描述符
void FD_ZERO(fd_set *set);//检查集合中指定的文件描述符是否可以读写
select的使用步骤
1 初始化,把当前的listenfd加入集合,初始化socketfd数组
2 监听数据,接受到connfd把它加入集合
3 循环处理文本,如果可读,读取长度,关闭sockfd
4 循环处理文本,读取指定长度的数据,将长度和数据写入data
5 往所有就绪的socketfd里面写入string
题外话,socket关闭后端口不可用
int sockfd;
int opt = 1;
int len = sizeof(opt);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, &len);
下面的
select编程的大致逻辑
1 设置 socket为非阻塞
2 建立死循环-可以不用这么做
3 初始化 fset
4 将当前的set加入到里面
5 循环遍历fset array---用于存储可用的soketfd的集合
6 使用select监控文件句柄
7 如果有fset,accpet接受socketfd,标识socketfd为可以读
8 集中处理所有的fset array,处理读和写
实例
服务端的例子
#include <iostream>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/types.h>
#include <fcntl.h>
//using namespace std;
#define BUFFER_SIZE 1024
#define HEAD_LEN 4
#define MAX_FD 1023
#define BUF_LEN 1000
void tsocket(int argc, const char * argv[]);
int main(int argc, const char * argv[]) {
tsocket(argc,argv);
return 0;
}
void tsocket(int argc, const char * argv[]){
if(argc < 3){
exit(-1);
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int backlog = atoi(argv[3]);
std::cout << "ip=" << ip << " port="<<port << " backlog=" << backlog << std::endl;
//声明信息
char sockBuf[BUFFER_SIZE];
int socketfd[MAX_FD];
char data[BUFFER_SIZE+HEAD_LEN];
//接受数据
size_t ret;
int fd;
int check_ret;
fd = socket(PF_INET,SOCK_STREAM , 0);
assert(fd >= 0);
//端口重用
int opt = 1;
int optLen = sizeof(opt);
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, optLen);
struct sockaddr_in address;
bzero(&address,sizeof(address));
//转换成网络地址
address.sin_port = htons(port);
address.sin_family = AF_INET;
//地址转换
inet_pton(AF_INET, ip, &address.sin_addr);
//绑定ip和端口
check_ret = bind(fd,(struct sockaddr*)&address,sizeof(address));
assert(check_ret >= 0);
//非阻塞的socket
int cflags = fcntl(fd,F_GETFL,0);
fcntl(fd,F_SETFL, cflags|O_NONBLOCK);
//创建监听队列,用来存放待处理的客户连接
check_ret = listen(fd, backlog);
assert(check_ret >= 0);
struct sockaddr_in addressClient;
socklen_t clientLen = sizeof(addressClient);
//读的fd_set
fd_set readset;
int sockArray[MAX_FD];
int i,j,len,n;
int connfd;
for(i=0;i<MAX_FD;++i){
sockArray[i] = 0;
}
while(1){
//初始化 readset
FD_ZERO(&readset);
//把当前的socket加入监控
FD_SET(fd,&readset);
//如果出现可读,加入监控
for(i=0;i<MAX_FD;++i){
if(sockArray[i] == 1) FD_SET(i,&readset);
}
//select监视的文件句柄数,视进程中打开的文件数而定
//一般设为你要监视各文件
if (select(1024, &readset, NULL, NULL, NULL) < 0)
{
perror("select error");
exit(-1);
}
//将可读放入就绪里面
if(FD_ISSET(fd,&readset)){
connfd = accept(fd,NULL,NULL);
sockArray[connfd] = 1;
printf("client %d connected\n", connfd);
}
//集中处理可读
for(i=0;i<MAX_FD;++i){
if((sockArray[i] == 1) && FD_ISSET(i,&readset) ){
len = read(i,sockBuf,BUF_LEN);
if(len < 0){
perror("read error");
exit(-1);
}
//如果没有数据就等待
if(len == 0){
close(i);
sockArray[i] = 0;
continue;
}
printf("client %d send %s\n", len, sockBuf);
//写入数据
write(i,sockBuf,len);
}
}
}
close(fd);
}
客户端的例子
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define BUF_LEN 1023
void tserver(int argc, const char * argv[]);
int main(int argc, const char * argv[]) {
tserver(argc,argv);
return 0;
}
void tserver(int argc, const char * argv[]){
std::cout << "t server" << std::endl;
if(argc < 3){
exit(-1);
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int backlog = atoi(argv[3]);
std::cout << "ip=" << ip << " port="<<port << " backlog=" << backlog << std::endl;
int fd;
int check_ret;
fd = socket(PF_INET,SOCK_STREAM , 0);
assert(fd >= 0);
struct sockaddr_in address;
bzero(&address,sizeof(address));
//转换成网络地址
address.sin_port = htons(port);
address.sin_family = AF_INET;
//地址转换
inet_pton(AF_INET, ip, &address.sin_addr);
check_ret = connect(fd, (struct sockaddr*) &address, sizeof(address));
assert(check_ret >= 0);
//发送数据
//const char* oob_data = "abc";
const char* normal_data = "my boy!";
send(fd, normal_data, strlen(normal_data), 0);
printf("send data len=%lu,msg=%s\n",strlen(normal_data),normal_data);
//接受数据
int len;
char sockBuf[BUF_LEN];
memset(sockBuf, '\0', BUF_LEN);
len = recv(fd, sockBuf, BUF_LEN-1, 0);
printf("receive data len=%d,msg=%s\n",len,sockBuf);
close(fd);
}
参考:
http://www.cnblogs.com/wenqiang/p/5508541.html