select的socket server多路复用模型

本文介绍了如何使用select系统调用来实现一个TCP Socket服务器的多路复用模型,通过示例代码展示了如何监听并处理多个客户端连接。相较于多进程,作者更倾向于使用多线程,因为多线程具有更低的代价且能共享数据。

除了上文将的通过fork多进程socket server模型外, 另一种方法是使用select系统调用。当然,也可以对上文的fork进行修改,例如使用pthread创建线程来完成具体工作。

两种方法各有优缺点,都不是最优解决方案。但没种解决方案都不是绝对的,我们可以混合使用各种解决方案来完成,关键是能够理解socket,select,多线程,多进程的本质。相对于多进程fork的方式,我更喜欢使用pthread多线程,原因之一是pthread的代价更低,更重要的原因是多线程可以共享数据,省去了多进程之间通信的代价。

多进程socket server例程:http://www.9say.com/2009/01/multi-process-socket-server-demo-example/

以下是使用select多路复用的socket server模型的一个例子:

/**

 *        @brief:        server.c

 *

 *        A simplest tcp connection example of server side.

 * use select to accept multi-client connections.

 *

 *        guotie.9(at)gmail.com

 *

 */

#include <stdio.h>

#include <string.h>

#include <signal.h>

 

#include <sys/select.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <netinet/in.h>

#include <errno.h>

#include <unistd.h>

 

#define TRANSFER_PORT               6688

#define MAX_LISTEN       12

 

int init_transfer();

void fini_transfer();

 

int recv_data(int sock, char *buf, int len);

 

int sig_handler(void);

void sig_handler_func(int sig);

int select_conns();

 

int transfer_sock;

 

int init_transfer()

{

        int sock = -1;

        struct sockaddr_in addr;

 

        printf(”init transfer ……/n”);

        sock =  socket(PF_INET, SOCK_STREAM, 0);

        if(-1 == sock)

        {

perror(”Failed to create socket./n”);

return -1;

        }

        memset(&addr, 0, sizeof(struct sockaddr_in));

 

        addr.sin_family =       AF_INET;

        addr.sin_addr.s_addr = inet_addr(”0.0.0.0″);

        addr.sin_port = htons(TRANSFER_PORT);

 

        if(-1 == bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)))

        {

perror(”Failed to bind socket./n”);

return -1;

        }

 

        if(-1 == listen(sock, MAX_LISTEN))

        {

printf(”Failed to bind socket. errno: %d/n”, errno);

return -1;

        }

 

        transfer_sock = sock;

 

        printf(”      %s: init transfer socket %d …… success/n”, __FUNCTION__, sock);

 

        return 0;

}

 

void fini_transfer()

{

        int ret;

 

        ret = shutdown(transfer_sock, SHUT_RDWR);

 

        ret = close(transfer_sock);

        if(ret < 0)

             perror(”/nfini_transfer: “);

        else

             printf(”/n/n      %s: success shutdown transfer socket!/n/n”, __FUNCTION__);

}

 

int recv_data(int sock, char *buf, int len)

{

        int l = 0;

 

        memset(buf, 0, len);

        l = recv(sock, buf, len, 0);

        if(0 > l)

        {

             perror(”recv error: “);

             return -1;

        }

        else if (0 == l)

        {

                printf(”client shutdown the socket %d … /n”, sock);

                return 0;

        }

        printf(”recv %d data./n”, l);

        return l;

}

 

void sig_handler_func(int sig)

{

        fini_transfer();

        exit(0);

}

 

int sig_handler(void)

{

        int ret = 0;

        struct sigaction sact;

 

        ret = sigemptyset(&sact.sa_mask);

        if(0 > ret)

            return ret;

 

        sact.sa_flags = 0;

        sact.sa_handler = sig_handler_func;

        ret = sigaction(SIGQUIT, &sact, NULL);

        if(0 > ret)

            return ret;

 

        sact.sa_flags = 0;

        sact.sa_handler = sig_handler_func;

        ret = sigaction(SIGINT, &sact, NULL);

        if(0 > ret)

            return ret;

 

        sact.sa_flags = 0;

        sact.sa_handler = sig_handler_func;

        ret = sigaction(SIGKILL, &sact, NULL);

        if(0 > ret)

            return ret;

 

        return 0;

}

 

 

int main()

{

    int i;

    int s;

    int ret = 0;

    char buf[4096] = { 0 };

    int len;

    int client_fd[MAX_LISTEN];

    int max_fd = 0;

 

    fd_set client_conns;

    struct sockaddr_in client_addr;

 

    init_transfer();

 

    sig_handler();

 

    for(i = 0; i < MAX_LISTEN; i ++)

        client_fd[i] = -1;

 

    FD_ZERO(&client_conns);

    max_fd = transfer_sock;

 

    while(1)

    {

/**

* 特别提醒,这里必须把所有正在listen的socket重新加入select队列,

* 和我之前理解的不同。如果不把该socket重新加入,则select无法

* 监听这个socket的状态变化,也就无法accept新的连接。

*/

FD_CLR(transfer_sock, &client_conns);

FD_SET(transfer_sock, &client_conns);

ret = select(max_fd + 1, &client_conns, NULL, NULL, (struct timeval *)0);

        if (0 > ret)

        {

            perror(”select(): “);

            exit(-1);

        }

else if (0 == ret)

{

printf(” select timeout ……/n”);

continue;

}

 

printf(”select return something … /n”);

 

        /**

         *        有新的客户连接

         */

        if(FD_ISSET(transfer_sock, &client_conns))

        {

            len = sizeof(struct sockaddr_in);

            memset(&client_addr, 0, len);

            s = accept(transfer_sock, (struct sockaddr *)&client_addr, &len );

            if(-1 == s)

            {

                perror(”accept failed:”);

                continue;

            }

            /**

             *        把新连接加入select队列中

             */

             for (i = 0; i < MAX_LISTEN; i ++)

             {

                if(-1 != client_fd[i])

                    continue;

                client_fd[i] = s;

 

                break;

             }

             FD_SET(s , &client_conns);

             if(max_fd < s)

                     max_fd = s;

printf(”Add client socket connection %d to select/n”, s);

             continue;

        }

 

        for(i = 0; i < MAX_LISTEN; i ++)

        {

            if(-1 == client_fd[i])

                continue;

            if(!FD_ISSET(client_fd[i], &client_conns))

                continue;

            ret = recv_data(client_fd[i], buf, sizeof(buf));

            if(0 >= ret)

            {

                close(client_fd[i]);

 

                FD_CLR(client_fd[i], &client_conns);

 

printf(”del socket %d from select pipe./n”, client_fd[i]);

                client_fd[i] = -1;

 

            }

        }

max_fd = transfer_sock;

for(i = 0; i <  MAX_LISTEN; i ++)

{

if(max_fd < client_fd[i])

{

max_fd = client_fd[i];

}

}

printf(”This cycle of select loop over, max_fd = %d…/n”, max_fd);

    }

 

        fini_transfer();

        return 0;

}

select的socket server多路复用模型

January 13, 2009 by admin · Leave a Comment
Filed under: program 

除了上文将的通过fork多进程socket server模型外, 另一种方法是使用select系统调用。当然,也可以对上文的fork进行修改,例如使用pthread创建线程来完成具体工作。

两种方法各有优缺点,都不是最优解决方案。但没种解决方案都不是绝对的,我们可以混合使用各种解决方案来完成,关键是能够理解socket,select,多线程,多进程的本质。相对于多进程fork的方式,我更喜欢使用pthread多线程,原因之一是pthread的代价更低,更重要的原因是多线程可以共享数据,省去了多进程之间通信的代价。

多进程socket server例程:http://www.9say.com/2009/01/multi-process-socket-server-demo-example/

以下是使用select多路复用的socket server模型的一个例子:

/**

 *        @brief:        server.c

 *

 *        A simplest tcp connection example of server side.

 * use select to accept multi-client connections.

 *

 *        guotie.9(at)gmail.com

 *

 */

#include <stdio.h>

#include <string.h>

#include <signal.h>

 

#include <sys/select.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#include <netinet/in.h>

#include <errno.h>

#include <unistd.h>

 

#define TRANSFER_PORT               6688

#define MAX_LISTEN       12

 

int init_transfer();

void fini_transfer();

 

int recv_data(int sock, char *buf, int len);

 

int sig_handler(void);

void sig_handler_func(int sig);

int select_conns();

 

int transfer_sock;

 

int init_transfer()

{

        int sock = -1;

        struct sockaddr_in addr;

 

        printf(”init transfer ……/n”);

        sock =  socket(PF_INET, SOCK_STREAM, 0);

        if(-1 == sock)

        {

perror(”Failed to create socket./n”);

return -1;

        }

        memset(&addr, 0, sizeof(struct sockaddr_in));

 

        addr.sin_family =       AF_INET;

        addr.sin_addr.s_addr = inet_addr(”0.0.0.0″);

        addr.sin_port = htons(TRANSFER_PORT);

 

        if(-1 == bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)))

        {

perror(”Failed to bind socket./n”);

return -1;

        }

 

        if(-1 == listen(sock, MAX_LISTEN))

        {

printf(”Failed to bind socket. errno: %d/n”, errno);

return -1;

        }

 

        transfer_sock = sock;

 

        printf(”      %s: init transfer socket %d …… success/n”, __FUNCTION__, sock);

 

        return 0;

}

 

void fini_transfer()

{

        int ret;

 

        ret = shutdown(transfer_sock, SHUT_RDWR);

 

        ret = close(transfer_sock);

        if(ret < 0)

             perror(”/nfini_transfer: “);

        else

             printf(”/n/n      %s: success shutdown transfer socket!/n/n”, __FUNCTION__);

}

 

int recv_data(int sock, char *buf, int len)

{

        int l = 0;

 

        memset(buf, 0, len);

        l = recv(sock, buf, len, 0);

        if(0 > l)

        {

             perror(”recv error: “);

             return -1;

        }

        else if (0 == l)

        {

                printf(”client shutdown the socket %d … /n”, sock);

                return 0;

        }

        printf(”recv %d data./n”, l);

        return l;

}

 

void sig_handler_func(int sig)

{

        fini_transfer();

        exit(0);

}

 

int sig_handler(void)

{

        int ret = 0;

        struct sigaction sact;

 

        ret = sigemptyset(&sact.sa_mask);

        if(0 > ret)

            return ret;

 

        sact.sa_flags = 0;

        sact.sa_handler = sig_handler_func;

        ret = sigaction(SIGQUIT, &sact, NULL);

        if(0 > ret)

            return ret;

 

        sact.sa_flags = 0;

        sact.sa_handler = sig_handler_func;

        ret = sigaction(SIGINT, &sact, NULL);

        if(0 > ret)

            return ret;

 

        sact.sa_flags = 0;

        sact.sa_handler = sig_handler_func;

        ret = sigaction(SIGKILL, &sact, NULL);

        if(0 > ret)

            return ret;

 

        return 0;

}

 

 

int main()

{

    int i;

    int s;

    int ret = 0;

    char buf[4096] = { 0 };

    int len;

    int client_fd[MAX_LISTEN];

    int max_fd = 0;

 

    fd_set client_conns;

    struct sockaddr_in client_addr;

 

    init_transfer();

 

    sig_handler();

 

    for(i = 0; i < MAX_LISTEN; i ++)

        client_fd[i] = -1;

 

    FD_ZERO(&client_conns);

    max_fd = transfer_sock;

 

    while(1)

    {

/**

* 特别提醒,这里必须把所有正在listen的socket重新加入select队列,

* 和我之前理解的不同。如果不把该socket重新加入,则select无法

* 监听这个socket的状态变化,也就无法accept新的连接。

*/

FD_CLR(transfer_sock, &client_conns);

FD_SET(transfer_sock, &client_conns);

ret = select(max_fd + 1, &client_conns, NULL, NULL, (struct timeval *)0);

        if (0 > ret)

        {

            perror(”select(): “);

            exit(-1);

        }

else if (0 == ret)

{

printf(” select timeout ……/n”);

continue;

}

 

printf(”select return something … /n”);

 

        /**

         *        有新的客户连接

         */

        if(FD_ISSET(transfer_sock, &client_conns))

        {

            len = sizeof(struct sockaddr_in);

            memset(&client_addr, 0, len);

            s = accept(transfer_sock, (struct sockaddr *)&client_addr, &len );

            if(-1 == s)

            {

                perror(”accept failed:”);

                continue;

            }

            /**

             *        把新连接加入select队列中

             */

             for (i = 0; i < MAX_LISTEN; i ++)

             {

                if(-1 != client_fd[i])

                    continue;

                client_fd[i] = s;

 

                break;

             }

             FD_SET(s , &client_conns);

             if(max_fd < s)

                     max_fd = s;

printf(”Add client socket connection %d to select/n”, s);

             continue;

        }

 

        for(i = 0; i < MAX_LISTEN; i ++)

        {

            if(-1 == client_fd[i])

                continue;

            if(!FD_ISSET(client_fd[i], &client_conns))

                continue;

            ret = recv_data(client_fd[i], buf, sizeof(buf));

            if(0 >= ret)

            {

                close(client_fd[i]);

 

                FD_CLR(client_fd[i], &client_conns);

 

printf(”del socket %d from select pipe./n”, client_fd[i]);

                client_fd[i] = -1;

 

            }

        }

max_fd = transfer_sock;

for(i = 0; i <  MAX_LISTEN; i ++)

{

if(max_fd < client_fd[i])

{

max_fd = client_fd[i];

}

}

printf(”This cycle of select loop over, max_fd = %d…/n”, max_fd);

    }

 

        fini_transfer();

        return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值