标签:
在写这段代码的时候,发现很多地方容易弄错。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);
这里的几个宏都是传入指针,而不是值传递。
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;
}
TCP IO复用 select并发服务端 Linux socket编程入门(3)
标签:
原文地址:http://www.cnblogs.com/sunfishgao/p/4969779.html