TCP socket select用法分析
2017-06-02 16:22
239 查看
网上一篇博主写的select,十分透彻,参考linux下多路复用模型之Select模型
本文主要记录了select一些用法和自己写的一个用select的服务器端例子。
首先,我们来看看select函数的定义和参数的含义:
参数含义:
1. nfds:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
2. readfds:(可选)指针,指向一组等待可读性检查的套接口。
3. writefds:(可选)指针,指向一组等待可写性检查的套接口。
4. exceptfds:(可选)指针,指向一组等待错误检查的套接口。
5. timeout:select()最多等待时间,对阻塞操作则为NULL。
返回值:
select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;如果超时则返回0;否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError获取相应错误代码。
1. 当返回为-1时,所有描述符集清0。
2. 当返回为0时,表示超时。
3. 当返回为正数时,表示已经准备好的描述符数。
select()返回后,在3个描述符集里,依旧是1的位就是准备好的描述符。这也就是为什么,每次用select后都要用FD_ISSET的原因。
select函数实现I/O多路复用,可以用来监视多个描述符,之后我们调用FD_ISSET函数确定具体是哪一个描述符准备好了。
那怎样才算准备好了呢?《unix环境高级编程》中,提到:
若对读集中的一个描述符进行的read操作不会阻塞,则认为此描述符是准备好的。
若对写集中的一个描述符进行的write操作不会阻塞,则认为此描述符是准备好的。
若对异常条件集中的一个描述符有一个未决异常条件,则认为此描述符是准备好的。
对于读、写和异常条件,普通文件的文件描述符总是认为准备好的。
操作select函数,还需要以下几个函数配合。
void FD_CLR(int fd, fd_set *set) // 清除set集合中描述符fd
int FD_ISSET(int fd, fd_set *set) //判断set集合中描述符fd是否准备好
void FD_SET(int fd, fd_set *set) //将描述符fd添加进集合set(其实是将某一位置1)。
void FD_ZERO(fd_set *set) //将set集全部清除
接下来看一个服务器端运用select函数的例子。从下面的程序可以看出,服务器端调用函数FD_SET( sockfd, &readfds)把套接字描述符sockfd加入读集合,调用select函数监听了socket套接字是否准备好。当客户端有连接请求时,select会返回一个正数,然后调用FD_ISSET(sockfd,&readfds)判断是哪一个描述符准备好。当然这里只能是socket,因为我们只向读集合添加了一个。接着调用accept接收请求,创建线程去处理客户端的请求。
本文主要记录了select一些用法和自己写的一个用select的服务器端例子。
首先,我们来看看select函数的定义和参数的含义:
int select( int nfds, fd_set FAR* readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout)
参数含义:
1. nfds:是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!在Windows中这个参数的值无所谓,可以设置不正确。
2. readfds:(可选)指针,指向一组等待可读性检查的套接口。
3. writefds:(可选)指针,指向一组等待可写性检查的套接口。
4. exceptfds:(可选)指针,指向一组等待错误检查的套接口。
5. timeout:select()最多等待时间,对阻塞操作则为NULL。
返回值:
select()调用返回处于就绪状态并且已经包含在fd_set结构中的描述字总数;如果超时则返回0;否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError获取相应错误代码。
1. 当返回为-1时,所有描述符集清0。
2. 当返回为0时,表示超时。
3. 当返回为正数时,表示已经准备好的描述符数。
select()返回后,在3个描述符集里,依旧是1的位就是准备好的描述符。这也就是为什么,每次用select后都要用FD_ISSET的原因。
select函数实现I/O多路复用,可以用来监视多个描述符,之后我们调用FD_ISSET函数确定具体是哪一个描述符准备好了。
那怎样才算准备好了呢?《unix环境高级编程》中,提到:
若对读集中的一个描述符进行的read操作不会阻塞,则认为此描述符是准备好的。
若对写集中的一个描述符进行的write操作不会阻塞,则认为此描述符是准备好的。
若对异常条件集中的一个描述符有一个未决异常条件,则认为此描述符是准备好的。
对于读、写和异常条件,普通文件的文件描述符总是认为准备好的。
操作select函数,还需要以下几个函数配合。
void FD_CLR(int fd, fd_set *set) // 清除set集合中描述符fd
int FD_ISSET(int fd, fd_set *set) //判断set集合中描述符fd是否准备好
void FD_SET(int fd, fd_set *set) //将描述符fd添加进集合set(其实是将某一位置1)。
void FD_ZERO(fd_set *set) //将set集全部清除
接下来看一个服务器端运用select函数的例子。从下面的程序可以看出,服务器端调用函数FD_SET( sockfd, &readfds)把套接字描述符sockfd加入读集合,调用select函数监听了socket套接字是否准备好。当客户端有连接请求时,select会返回一个正数,然后调用FD_ISSET(sockfd,&readfds)判断是哪一个描述符准备好。当然这里只能是socket,因为我们只向读集合添加了一个。接着调用accept接收请求,创建线程去处理客户端的请求。
/*server.c*/ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <unistd.h> #include <netinet/in.h> #define BUFFER_SIZE 4096 #define MAX_QUE_CONN_NM 5 #define PORT 6000 //#define MAXSOCKFD 10 #define FILE_NAME_MAX 512 void recv_mul_file(int sockfd); void* pthread_func(void * arg); int main(int argc,char* argv[]) { int sockfd; int sin_size = sizeof(struct sockaddr); struct sockaddr_in server_sockaddr, client_sockaddr; int i = 1;/* 使得重复使用本地地址与套接字进行绑定 */ /*建立socket连接*/ if ((sockfd = socket(AF_INET,SOCK_STREAM,0))== -1) { perror("socket"); exit(1); } printf("Socket id = %d\n",sockfd); /*设置sockaddr_in 结构体中相关参数*/ server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_sockaddr.sin_zero), 8); setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); /*绑定函数bind*/ if (bind(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr))== -1) { perror("bind"); exit(1); } printf("Bind success!\n"); /*调用listen函数*/ if (listen(sockfd, MAX_QUE_CONN_NM) == -1) { perror("listen"); exit(1); } printf("Listening....\n"); recv_mul_file(sockfd); close(sockfd); return 0; } void recv_mul_file(int sockfd) { fd_set readfds; FD_ZERO(&readfds); FD_SET( sockfd, &readfds); pthread_t tid; struct sockaddr_in client_sockaddr; int client_fd, sin_size = sizeof(struct sockaddr); int MAXSOCKFD = sockfd; while(1) { if(select(MAXSOCKFD + 1,&readfds,NULL,NULL,NULL) > 0) { if(FD_ISSET(sockfd,&readfds)>0) { if ((client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, (socklen_t *)&sin_size)) == -1) { perror("accept"); exit(1); } pthread_create(&tid, NULL, pthread_func, &client_fd); } FD_SET( client_fd, &readfds); MAXSOCKFD = (MAXSOCKFD > client_fd? MAXSOCKFD:client_fd); } } } void* pthread_func(void * arg) { //recv file imformation int client_fd; char buff[BUFFER_SIZE]; char filename[FILE_NAME_MAX]; int count; bzero(buff,BUFFER_SIZE); client_fd = *(int *)arg; printf("recv from client,client_fd = %d\n",client_fd); count=recv(client_fd,buff,BUFFER_SIZE,0); //把接受到到字符放在长度为BUFFER_SIZE的buff地址上,接收成功返回接收到到字节数目 if(count<0) { perror("recv"); exit(1); } strncpy(filename,buff,strlen(buff)>FILE_NAME_MAX?FILE_NAME_MAX:strlen(buff));//把filename地址上的内容复制到地址buff上,第三个参数表明复制多少个字节 printf("Preparing recv file : %s\n",filename ); //recv file FILE *fd=fopen(filename,"wb+"); //告诉函数库,打开的是一个二进制到可写文件,地址在指针filename if(NULL==fd) { perror("open"); exit(1); } bzero(buff,BUFFER_SIZE); //缓冲区清0 int length=0; while(length=recv(client_fd,buff,BUFFER_SIZE,0)) //这里是分包接收,每次接收4096个字节 { if(length<0) { perror("recv"); exit(1); } int writelen=fwrite(buff,sizeof(char),length,fd);//把从buff接收到的字符写入(二进制)文件中 if(writelen<length) { perror("write"); exit(1); } bzero(buff,BUFFER_SIZE); //每次写完缓冲清0,准备下一次的数据的接收 } printf("Receieved file:%s finished!\n",filename ); fclose(fd); close(client_fd); return 0; }
相关文章推荐
- socket中tcp 和 udp的基本用法
- TCP socket, select model in linux.
- linux非阻塞式socket编程之select()用法
- 内核中的TCP的追踪分析-3-TCP(IPV4)的socket的地址绑定
- 内核中的TCP的追踪分析-2-追踪TCP(IPV4)的socket的创建
- socket select()用法
- socket编程:select 用法 例子
- 3个学习Socket编程的简单例子:TCP Server/Client, Select
- 基本的TCP/IP Socket用法(二)
- TCP socket, select model in linux
- 基本的TCP/IP Socket用法(二)
- 基本的TCP/IP Socket用法(一)
- socket UDP TCP 的一些用法
- Linux TCP Socket程序分析
- linux socket select 函数分析
- Delph7中TcpClient和TcpServer用法分析
- 3个学习Socket编程的简单例子:TCP Server/Client, Select
- socket select()用法
- socket select()用法