TCP IO复用 select并发服务端 Linux socket编程入门(3)

在写这段代码的时候,发现很多地方容易弄错。select有可能会出错,返回-1。

比如

int  FD_ISSET(int fd,fd_set *fdset);
void FD_CLR(int fd,fd_set *fdset);
void FD_SET(int fd,fd_set *fdset);
void FD_ZERO(int fd,fd_set *fdset);

这里的几个宏都是传入指针,而不是值传递。

 

 select函数声明如下:
int select(int maxfdp1,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

用法:

  1.首先定义好fd_set好结构体,并用宏FD_ZERO进行清零处理。

  2.用FD_SET将结构体和listenfd绑定。

  3.调用select。(select会一直堵塞在这里,监听listenfd或者sockfd(三次握手的信息或者传来的数据))

  4.检测传来的数据是否是三次握手,如果是,则建立连接。

  5.用for从零开始遍历套接字,检测传来的数据是否是消息,如果是,则接收。

代码如下:

#include<stdio.h>  
#include<stdlib.h>  
#include<string.h>  
#include<sys/socket.h>  
#include<sys/types.h>  
#include<unistd.h>  
#include<netinet/in.h>  
#include <errno.h>    

#define MAX_LISTEN_QUE 10
#define MAX_BUFF_LINE 100
#define PORT 9999  

int create_socket();
int process_data(int sockfd,  fd_set *global_rdfs);

int main(int argc, char **argv){
	int listenfd, sockfd;
	int i, maxfd;
	struct sockaddr_in cli_addr;
	int cli_addr_len;
	int err;
	char buff[MAX_BUFF_LINE];
	int bytes;
	
	//声明了两个fdset变量,
	fd_set global_rdfs,current_rdfs;
	
	listenfd = create_socket();
	//清零
	FD_ZERO(&global_rdfs);
	//将listenfd和global_rdfs相关联。
	FD_SET(listenfd, &global_rdfs);
	maxfd = listenfd;
	
	cli_addr_len = sizeof(cli_addr);
	
	for(;;){
		//因为global_rdfs要时刻关注listenfd
		//为了保证对fd_set结构体读写以后还能在下次for循环不出错误
		//这里这里用一个替身。
		current_rdfs = global_rdfs;
		
		if((err = select(maxfd + 1, &current_rdfs, NULL, NULL, NULL))< 0){
			//程序会堵塞在select里,直到有数据来袭
			printf("select err:%d",err);
			return -1;
		}
		
		if(FD_ISSET(listenfd, &current_rdfs)){
			//如果传来的是三次握手的数据,就建立连接
			if((sockfd = accept(listenfd, (struct sockaddr *)&cli_addr, &cli_addr_len))<0){
				perror("accept error.\n");
				return -1;
			}
			printf("产生了新的连接(sockfd:%d)\n", sockfd);
			FD_CLR(i, &current_rdfs);
			maxfd = maxfd > sockfd ? maxfd :sockfd;
			FD_SET(sockfd, &global_rdfs);
			continue;
		}
		
		//遍历从0到max,寻找传来的消息数据
		for(i = 0; i <= maxfd; i++){
			if(FD_ISSET(i, &current_rdfs)){
				
				printf("产生了新的数据:");
				printf("process_data\n");
				printf("i数据:%d", i);
                   //注意,这里操作的对象一定要是global_rdfs
                   //如果写成current_rdfs,则会select出错 process_data(i, &global_rdfs); } } } return 0; } int process_data(int sockfd, fd_set *global_rdfs){ char buff[MAX_BUFF_LINE]; int bytes; bzero(buff, MAX_BUFF_LINE); bytes = recv(sockfd, buff, MAX_BUFF_LINE, 0); if(bytes < 0){ printf("recv err:"); return -1; } if(bytes == 0 ){ FD_CLR(sockfd, global_rdfs); close(sockfd); printf("recv:null data:"); return 0; } if(!strcmp(buff, "q")){ //这里有个自定的小规矩,就是当传来q的时候,就退出连接 FD_CLR(sockfd, global_rdfs); close(sockfd); printf("quit!\n"); return 0; } buff[bytes] = '\0'; printf("%s\n", buff); send(sockfd, buff, bytes, 0); return 0; } int create_socket(){ int listenfd, err; int len; struct sockaddr_in servaddr; int ret, opt = 1; listenfd = socket(AF_INET, SOCK_STREAM, 0); if(listenfd < 0){ printf("socket err:"); return -1; } //将listenfd变成非阻塞 if((ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) < 0){ printf("Error, set socket reuse addr failed"); return -1; } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(PORT); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); len = sizeof(struct sockaddr); err = bind(listenfd, (struct sockaddr *)&servaddr, len); if(err < 0){ printf("bind err:"); return -1; } err = listen(listenfd, MAX_LISTEN_QUE); if(err < 0){ printf("listen err:"); return -1; } return listenfd; }

  

 

 
posted @ 2015-11-16 19:03  chunyi  阅读(349)  评论(0编辑  收藏  举报