您的位置:首页 > 其它

select的socket server多路复用模型

2009-04-22 10:16 567 查看
除了上文将的通过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;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: