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, ¤t_rdfs, NULL, NULL, NULL))< 0){ //程序会堵塞在select里,直到有数据来袭 printf("select err:%d",err); return -1; } if(FD_ISSET(listenfd, ¤t_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, ¤t_rdfs); maxfd = maxfd > sockfd ? maxfd :sockfd; FD_SET(sockfd, &global_rdfs); continue; } //遍历从0到max,寻找传来的消息数据 for(i = 0; i <= maxfd; i++){ if(FD_ISSET(i, ¤t_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; }