您的位置:首页 > 运维架构 > Linux

linux下多路复用模型之Select模型

2015-08-06 11:12 609 查看
Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型)

Select模型极其作用:这文章讲述的很好,没必要重述已有的东西,就直接给链接

http://blog.csdn.net/turkeyzhou/article/details/8609360
我的理解:

/* According to POSIX.1-2001 */
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);


对于

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
第一个参数 nfds:  第n个文件id的编号   (linux下,一切皆文件)  需要注意的是: nfds = fd+1 (fd 为 FD_SET中的fd)
第二个参数:  fd_set *readfds   读取文件编号,如果不需要读取的话 可以设置为NULL
第三 ,四个参数:  同上
第五个参数:为一个定义超时的结构体


struct timeval {
time_t         tv_sec;     /* seconds */
suseconds_t    tv_usec;    /* microseconds */
};



该结构用来设定多少时间为超时 ,比如

struct timeval ss ;
ss.tv_sec  =3;
ss.tv_usec =0;
//表示设定为3秒后为超时,select将会返回0


对于下面这几个函数:

void FD_CLR(int fd, fd_set *set);

用来清除fd的fd_set ,比如fd为5 ,则表示set集中所有设定等于5的fd_set 都将被清除

int FD_ISSET(int fd, fd_set *set);

判断是否set 与fd是否绑定,如果没有绑定,则返回false,如果绑定了则返回True

void FD_SET(int fd, fd_set *set);


将fd值 和set绑定

void FD_ZERO(fd_set *set);


将set集全部清除

简单的例子:

判断是否有数据输入,有则直接打印出来

#include<stdio.h>
#include<string.h>
#include<sys/select.h>
#include<unistd.h>
#include<sys/time.h>
#include<sys/types.h>
#define maxn 6
#define EXIT_FAILURE  -1
#define EXIT_SUCCESS   0

int main(int argc , char * argv []){

fd_set mtfd ;
int ffd =0;
struct timeval  outtime ;
int retval  ;
char redbuf[maxn];

FD_ZERO(&mtfd);
FD_SET(ffd , &mtfd);

// wait up to 5.5 s
outtime.tv_sec = 5 ;
outtime.tv_usec = 500 ;
retval = select(ffd+1 , &mtfd ,NULL , NULL , &outtime);
if(-1 == retval )
{
printf("error happened ! %d \n" ,__LINE__ );
return EXIT_FAILURE ;
}
else if(0 == retval ){
printf(" timeout !!  %d \n" ,__LINE__ );
return  EXIT_FAILURE ;
}
//means that is good !

printf("retval  =  %d \n", retval);

if( FD_ISSET(ffd , &mtfd) ){

memset(redbuf ,0 , sizeof(redbuf));
printf("reading ... !! ");
// read(1 , redbuf ,sizeof(redbuf) ); //use the sys func
fread(redbuf , sizeof(redbuf) ,ffd+1 , stdin );
}
//  fwrite(redbuf ,strlen(redbuf) , 1 , stdout );
//  write(1 , redbuf , strlen(redbuf));
printf("buf = %s    buf_len = %d \n" , redbuf , strlen(redbuf));

return EXIT_SUCCESS ;
}


View Code
makefile文件:

.SUFFIXES: .o.c
CC =gcc
SRC = Se_keyboard.c
OBJ = $(SRC: .c =.o)
BIN = Se_keyboard

.PHONY: start
start:  $(OBJ)
$(CC) -o $(BIN) $(OBJ)
.o.c: $(SRC)
$(CC) -g -Wall $@ -c  $<
.PHONY: clean
clean:
rm -f $(OBJ)




虽然知道这么多,但是还是觉得Select并没有什么作用。

Select一般是和Socket搭配使用,相当于线程池的效果,但是线程池有缺点,详情看这儿:

/article/2797639.html

/article/1339514.html

关于Select的原理图:

首先来看下一对一的socket的c/s模式



关于socket的一对一的详解

/article/5255432.html

看完这个之后,我们现在可以来看看这个图:



下面为举例:

服务器创建一个socket并bind绑定一个本机地址和设定一个端口,然后进入listen监听状态。采用select模型而非传统apache模型(ppc)或者tpc模型 。 不过Select模型就是有这样一个特点

一般我们default默认的SOMAXCONN为128 当然我们可以另外取一个设定(下面我们设定的是2048)作为最大连接数,虽然可以设置更大,但是缺点是,select模型是一个轮询模式,就是每一个都需要遍历一边所有的链接的fd

查看是否在fd_set集合中,这样,当SOMAXCONN取值非常大时,对于每一个客户端,访问时间都会延迟一点点,这样就是效率不是特别高!

下面是一个简单的多路复用的网络并发Select模型

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<netinet/in.h>
#include<netinet/ip.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/time.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<assert.h>

#ifndef  EXIT_SUCCESS
#define EXIT_SUCCESS 0
#endif

#ifndef  EXIT_FAILURE
#define EXIT_FAILURE -1
#endif

#define Max_connect 2048    //usually is SOMAXCONN =128 ,can be change

#define maxn 1024
#define Port 5567
#define Ser_addr "192.168.132.128"

#define ERROR_EXIT( inf ) \
do{    \
perror( inf );  \
printf("it happened in %d \n", __LINE__); \
exit(-1); \
}while(0);

#define  Waring( inf ) \
do{    \
perror( inf );  \
printf("it happened in %d \n", __LINE__); \
}while(0);

int fds[Max_connect];
int cnt = 1;

void
print (int fd, const char *str)
{
assert (str != NULL);
printf ("the fd is %d \n", fd);
puts (str);
}

int
main (int argv, char *argc[])
{

int ser_sfd = -1, i;
struct sockaddr_in ser_addr;
struct sockaddr_in client_addr;
int setfd = 0, optval, maxsockfd;

char rebuf[maxn], wbuf[maxn];

// build a socket with ipv4 ans tcp

if ((ser_sfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
ERROR_EXIT ("socket...!");

// set the type socket
/*
level ={ SOL_SOCKET ,IPPROTO_TCP}
setsockopt is to cancle the jiangsi process
*/

printf ("ser_sfd = %d  \n", ser_sfd);
if (setsockopt (ser_sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (optval))
< 0)
ERROR_EXIT ("setsockopt...!");

memset (&client_addr, 0, sizeof (client_addr));
memset (&ser_addr, 0, sizeof (ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons (Port);
ser_addr.sin_addr.s_addr = htonl (INADDR_ANY);

if (bind (ser_sfd, (struct sockaddr *) &ser_addr, sizeof (ser_addr)) < 0)
ERROR_EXIT ("bind..!");

if (listen (ser_sfd, Max_connect) < 0)
ERROR_EXIT ("listen...!");

//user the select moduel
memset (fds, 0, sizeof (fds));
fd_set fdset, wfd;
maxsockfd = ser_sfd;        //max socket fd

struct timeval tout;

tout.tv_sec = 15;
tout.tv_usec = 0;

while (1)
{

FD_ZERO (&fdset);        //clear
//FD_ZERO (&wfd);

FD_SET (ser_sfd, &fdset);    //bind
//FD_SET (ser_sfd, &wfd);

struct timeval tout;

tout.tv_sec = 15;
tout.tv_usec = 0;

for (i = 0; i < cnt; i++)
{
if (fds[i] != 0)
FD_SET (fds[i], &fdset);
}

int tag = select (maxsockfd + 1, &fdset, NULL, NULL, &tout);

if (tag == 0)
{
Waring ("select wait timeout !");
continue;
}
else if (tag == -1)
ERROR_EXIT ("Error select ...!");

//lunxun select
for (i = 0; i < cnt; i++)
{

if (FD_ISSET (fds[i], &fdset))
{

int len = recv (fds[i], rebuf, sizeof (rebuf), 0);
if (len <= 0)
{

printf ("%d: \n", fds[i]);
close (fds[i]);
FD_CLR (fds[i], &fdset);
Waring ("client is closed !");
continue;
}

printf ("the client_ip : %s\n",
inet_ntoa (client_addr.sin_addr));

print (fds[i], rebuf);

send (fds[i], rebuf, sizeof (rebuf), 0);    //hui she
memset (rebuf, 0, sizeof (rebuf));
}
}
//if have a new connect happened
//  memset(&client_addr , 0 ,sizeof(client_addr));
if (FD_ISSET (ser_sfd, &fdset))
{
// memset(&client_addr , 0 ,sizeof(client_addr));
int acplen = sizeof (client_addr);
int acp = accept (ser_sfd, (struct sockaddr *) &client_addr,
&acplen);

printf ("accept return acp=%d \n", acp);

if (acp < 0)
{

Waring ("waring accept acp<=0!");
continue;
}
//add to arr
if (cnt < maxn)
fds[cnt++] = acp;
else
{
ERROR_EXIT ("cnt>maxn");
}
if (acp > maxsockfd)
maxsockfd = acp;
}
}

for (i = 0; i < cnt; i++)
{
close (fds[i]);
}

return EXIT_SUCCESS;
}


makefile文件:

.SUFFIXES: .o.c
CC =gcc
SRC =  server.c
OBJ = $(SRC: .c =.o)
BIN = Sez_Server

.PHONY: start
start:  $(OBJ)
$(CC) -o $(BIN) $(OBJ)
.o.c: $(SRC)
$(CC) -g -Wall $@ -c  $<
.PHONY: clean
clean:
rm -f $(OBJ)


客户端:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<assert.h>
#include<unistd.h>
#ifndef EXIT_FAILURE
#define EXIT_FAILURE  -1    //exit failure
#endif
#ifndef EXIT_SUCCESS
#define EXIT_SUCCESS   0    // exit sucessful
#endif

#define Port 5567
#define IPADDR  "192.168.132.128"
#define maxn 1024

#define ERROR_EXIT( inf ) \
do{  \
perror( inf ); \
printf("it's happened in %d \n",__LINE__); \
}while(0) ;

//use the function to connect the server !!

int
main (int argv, char *argc[])
{

int sfd = -1;
char rbuf[maxn], wbuf[maxn];
// int sfd=-1 ; //socket_fd
int confd = -1;        //connect_fd
struct sockaddr_in soaddr;
if ((sfd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
ERROR_EXIT ("socket");
return EXIT_FAILURE;
}

//memset  a struct
memset (&soaddr, 0, sizeof (soaddr));

//int the struct sockaddr_in

soaddr.sin_family = AF_INET;
soaddr.sin_port = htons (Port);
soaddr.sin_addr.s_addr = inet_addr (IPADDR);

if ((confd =
connect (sfd, (struct sockadrr *) &soaddr, sizeof (soaddr))) < 0)
{
ERROR_EXIT ("connect");
return EXIT_FAILURE;
}

printf ("connect is sucessful !\n");

while (fgets (wbuf, sizeof (rbuf), stdin) != NULL)
{
write (sfd, wbuf, strlen (wbuf));
read (sfd, rbuf, sizeof (rbuf));
fputs (rbuf, stdout);
}

close (sfd);
close (confd);
return EXIT_SUCCESS;
}


makefile文件:

.SUFFIXES: .o.c
CC =gcc
SRC = client.c
OBJ = $(SRC: .c =.o)
BIN = Se_client

.PHONY: start
start:  $(OBJ)
$(CC) -o $(BIN) $(OBJ)
.o.c: $(SRC)
$(CC) -g -Wall $@ -c  $<
.PHONY: clean
clean:
rm -f $(OBJ)


效果图:





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