socket select用法:100行代码实现网络数据转发程序

无聊学习一下select+blocking socket的用法。不使用多线程+blocking socket的方法。

程序从本地1998端口转发到221.130.162.247的80端口:

#include <stdio.h>
#include <winsock.h>
#include <wininet.h>

WSADATA wsaData;
#define client_count 100
int clients[client_count] = {0};
int remotes[client_count] = {0};
static void remove_client(int i)
{
    shutdown(clients[i], SD_RECEIVE);
    closesocket(clients[i]);
    shutdown(remotes[i], SD_RECEIVE);
    closesocket(remotes[i]);
    clients[i] =
    remotes[i] = 0;
}
static int get_client()
{
    int i;
    for( i = 0; i<client_count; i++)
        if(!clients[i])
            return i;
    return 0;
}
int main(int argc, char **argv)
{
    fd_set fdreads, fdwrites;
    const int buf_size = 1024*32;
    char buf[buf_size];
    int ret = WSAStartup(MAKEWORD(2,1), (WSADATA*)&wsaData );
    if(ret!=0)
        perror( "failed to initialize win32 WSA" );
    int sock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
    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( 1998 );
    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("listen\n");
    for(;;){
        int i, j, k;
        FD_ZERO(&fdreads);
        FD_SET( sock, &fdreads);
        for( i=0; i<client_count; i++)
            if(clients[i]){
                FD_SET( clients[i], &fdreads );
                FD_SET( remotes[i], &fdreads );
            }
        ret = select(0, &fdreads, 0, 0, 0);
        printf("select ret=%d\n", ret);
        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);
                clients[j] = accept(sock, 0, 0);
                remotes[j] = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
                addr.sin_port = htons( 0 );
                if( bind( remotes[j], (struct sockaddr*)&addr, sizeof(struct sockaddr_in) ) < 0 )
                    exit(errno);
                if( connect( remotes[j], (struct sockaddr*)&remote_addr, sizeof(struct sockaddr_in) ) < 0)
                    exit(errno);
            }
            for( i=0; i<client_count; i++){
                if(!clients[i]) continue;
                if(FD_ISSET(clients[i], &fdreads) ){
                    int ret = recv(clients[i], buf, buf_size, 0);
                    printf("client_read_ret=%d\n", ret);
                    if( ret > 0 )
                        send( remotes[i], buf, ret, 0 );
                    if( ret <= 0 )
                        remove_client(i);
                }
                if(FD_ISSET(remotes[i], &fdreads) ){
                    int ret = recv(remotes[i], buf, buf_size, 0);
                    printf("remote_read_ret=%d\n", ret);
                    if( ret > 0 )
                        send( clients[i], buf, ret, 0 );
                    if( ret <= 0 )
                        remove_client(i);
                }
            }
        }
    }
    return 0;
}

socket select用法:100行代码实现网络数据转发程序》有5个想法

  1. Xiaoxia

    除了select,还有啥好方法能单个线程管理多个socket? 如果1000个socket呢?epoll只能在linux,iocp只能在windows?最好跨平台吧。。。

    回复
  2. Pingback引用通告: I/O复用之使用select完成端口转发功能 | 秣马儿的博客

发表回复

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

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