使用select+非阻塞socket写的网络数据转发程序

    从实践之中,我又学到东西了!使用select的时候,无论是使用非阻塞还是阻塞socket,调用recv和send函数返回0都意味着socket被远程关闭!!!

    select+阻塞socket版本请见  http://xiaoxia.org/2675.html

    对比两个版本,从理论上可以知道select+非阻塞socket要高效得多。在与远程服务器connect的时候,程序要等待连接完全建立完毕才返回,这里会让程序产生延迟。而在非阻塞中不会等待,直接去处理其它连接。所以,我打算在fox3(http://code.google.com/p/icefox)的开发中使用select+非阻塞socket。至于为何不使用epoll或者iocp是因为我考虑了可移植性。

 

   下面是代码,看球赛去!

 

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>

#define closesocket close
#define client_count 100
#define BUFFER_SIZE 8192

struct connection{
	int	clientfd;
	int remotefd;
	char clientbuf[BUFFER_SIZE];
	char remotebuf[BUFFER_SIZE];
	int clientbuf_size;
	int remotebuf_size;
} conns[client_count] = {0};

static void remove_client(int i)
{
	shutdown(conns[i].clientfd, SHUT_RD);
	closesocket(conns[i].clientfd);
	shutdown(conns[i].remotefd, SHUT_RD);
	closesocket(conns[i].remotefd);
	conns[i].clientfd =
	conns[i].remotefd = 0;
}

static int get_client()
{
	int i;
	for( i = 0; i<client_count; i++)
		if(!conns[i].clientfd)
			return i;
	return 0;
}

static int set_nonblocking(int sock)
{
	int opts;
	opts = fcntl(sock, F_GETFL);
	if(opts < 0)
		return -1;
	opts = opts | O_NONBLOCK;
	if(fcntl(sock, F_SETFL, opts) < 0)
		return(-1);
	return 0;
}

int main(int argc, char **argv)
{
	fd_set fdreads, fdwrites;
	const int buf_size = 1024*32;
	int ret;
	char buf[buf_size];
	int sock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
	ret = 1;
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&ret, sizeof(ret)); //端口复用
	struct sockaddr_in addr = {0};
	struct sockaddr_in remote_addr = {0};
	addr.sin_family = remote_addr.sin_family = PF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons( 1080 );
	remote_addr.sin_addr.s_addr = inet_addr("221.130.162.247");
	remote_addr.sin_port = htons( 80 );
	if( bind( sock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in) ) < 0 )
		perror("failed to bind socket");
	listen( sock , 5);
	printf("listening\n");
	for(;;){
		int i, j, k, lastfd=0;
		FD_ZERO(&fdreads);
		FD_ZERO(&fdwrites);
		FD_SET( sock, &fdreads);
		for( i=0; i<client_count; i++)
			if(conns[i].clientfd){
				if( conns[i].clientbuf_size == 0 ){
					FD_SET( conns[i].clientfd, &fdreads );
				}else{
					FD_SET( conns[i].remotefd, &fdwrites );
				}
				if( conns[i].remotebuf_size == 0){
					FD_SET( conns[i].remotefd, &fdreads );
				}else{
					FD_SET( conns[i].clientfd, &fdwrites );
				}
			}
		ret = select(client_count*2+2, &fdreads, &fdwrites, 0, 0);
		switch(ret){
		case -1:
			perror("error");
			break;
		case 0:
			perror("timeout?");
			break;
		default:
			if(FD_ISSET(sock, &fdreads)){
				int j = get_client();
				printf("accept %d\n", j);
				int len = sizeof(struct sockaddr_in);
				conns[j].clientfd = accept(sock, 0, 0);
				conns[j].remotefd = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
				addr.sin_port = htons( 0 );
				set_nonblocking(conns[j].clientfd);
				set_nonblocking(conns[j].remotefd);
				bind( conns[j].remotefd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in) );
				connect( conns[j].remotefd, (struct sockaddr*)&remote_addr, sizeof(struct sockaddr_in) );
			}
			for( j=0; j<client_count; j++){
				if(!conns[j].clientfd) continue;
				if(FD_ISSET(conns[j].clientfd, &fdreads) ){
					int ret = recv(conns[j].clientfd, conns[j].clientbuf, BUFFER_SIZE, 0);
					if( ret > 0 )
						conns[j].clientbuf_size = ret;
					if( ret <= 0 )
						remove_client(j);
				}
				if(FD_ISSET(conns[j].remotefd, &fdreads) ){
					int ret = recv(conns[j].remotefd, conns[j].remotebuf, BUFFER_SIZE, 0);
					if( ret > 0 )
						conns[j].remotebuf_size = ret;
					if( ret <= 0 )
						remove_client(j);
				}
				if(FD_ISSET(conns[j].clientfd, &fdwrites) ){
					int ret = send(conns[j].clientfd, conns[j].remotebuf, conns[j].remotebuf_size, 0);
					if( ret > 0 )
						conns[j].remotebuf_size -= ret;
					if( ret <= 0 )
						remove_client(j);
				}
				if(FD_ISSET(conns[j].remotefd, &fdwrites) ){
					int ret = send(conns[j].remotefd, conns[j].clientbuf, conns[j].clientbuf_size, 0);
					if( ret > 0 )
						conns[j].clientbuf_size -= ret;
					if( ret <= 0 )
						remove_client(j);
				}
			}
		}
	}
	return 0;
}



使用select+非阻塞socket写的网络数据转发程序》有6个想法

  1. Xiaoxia

    @Heiher:谢谢!我之前尝试过,不过好像还没有学会。。。 如果对性能要求不是很苛刻,为了便于跨平台,我现在还是优先使用select。

    回复

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据