1. 程式人生 > >1高併發伺服器:多路IO之select

1高併發伺服器:多路IO之select



1 select

Aselect能監聽的檔案描述符個數受限於FD_SETSIZE,一般為1024,單純改變程序開啟

的檔案描述符個數並不能改變select監聽檔案個數

B:解決1024以下客戶端時使用select是很合適的,但如果連結客戶端過多,select採用的是輪詢模型,會大大降低伺服器響應效率,不應在select上投入更多精力

2 依賴的標頭檔案

#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);

說明:

nfds: 監控的檔案描述符集裡最大檔案描述符加1,因為此引數會告訴核心檢測前多少個檔案描述符的狀態

readfds:監控有讀資料到達檔案描述符集合,傳入傳出引數

writefds:監控寫資料到達檔案描述符集合,傳入傳出引數

exceptfds:監控異常發生達檔案描述符集合,如帶外資料到達異常,傳入傳出引數

timeout:

定時阻塞監控時間,3中情況

  1. NULL,永遠等下去

  2. 設定timeval,等待固定時間

  3. 設定timeval裡時間均為0,檢查描述字後立即返回,輪詢。

struct timeval {

long tv_sec; /* seconds */

微秒;一百萬分之一秒*/

};

void FD_CLR(int fd,fd_set *set); 把檔案描述符集合裡fd清零

int FD_ISSET(int fd,fd_set *set); 測試檔案描述符集合裡fd是否置1

void FD_SET(int fd,fd_set *set); 把檔案描述符集合裡fd位置1

void FD_ZERO(fd_set *set);

把檔案描述符集合裡所有位清0

select模型

案例說明:

Server.c

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<netinet/in.h>

#include<arpa/inet.h>

#include<ctype.h>

#include<unistd.h>

#include"wrap.h"

#define MAXLINE 80

#define SERV_PORT 8000

int main(void)

{

int i,maxi,maxfd,listenfd,connfd,sockfd;

/*

* nreadyselect之後返回的被監控管理的數量,

* 最大的被管理的檔案最大的被管理的檔案描述符的

* 數量是1024

*/

/*FD_SETSIZE預設為1024*/

int nready,client[FD_SETSIZE]; /* FD_SETSIZE預設為1024 */

ssize_t n;

fd_set rset,allset;

char buf[MAXLINE];

/*下面表示的是ip地址的長度*/

char str[INET_ADDRSTRLEN];/*#define IN*/

socklen_t cliaddr_len;

struct sockaddr_in cliaddr,servaddr;

/*整個伺服器只有一個listenfd檔案描述符*/

//1、建立一個socket,案例中是針對TCP

listenfd = Socket(AF_INET,SOCK_STREAM,0);

bzero(&servaddr,sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

servaddr.sin_port = htons(SERV_PORT);

//2Bind

Bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));

//3、正在連結的時候最大支援128個,當accept之後就不管了

Listen(listenfd,20); /*預設最大128*/

maxfd = listenfd;/*初始化*/

maxi = -1;/*client[]的下標*/

for(i = 0;i < FD_SETSIZE;i++){

client[i] = -1;/*-1初始化client*/

}

//將管理的檔案描述符集合的內容設定成為0

FD_ZERO(&allset);

//listenfd納入allset中進行管理

FD_SET(listenfd,&allset);/*構造select監控檔案描述符集*/

for(;;){

/*每次迴圈時都重新設定select監控訊號集,這裡相當於rset的一個備份*/

rset = allset;

//第一個引數是最大描述的最大值+1

nready = select(maxfd+1,&rset,NULL,NULL,NULL);

if(nready < 0) {

perr_exit("select error");

}

//判斷listenfd是否已經納入管理

if(FD_ISSET(listenfd,&rset)) {

cliaddr_len = sizeof(cliaddr);

connfd = Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);

//列印ip地址和埠號

printf("received from %s at PORT %d\n",

inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,str,sizeof(str)),

ntohs(cliaddr.sin_port));

for(i = 0;i < FD_SETSIZE;i++) {

if(client[i] < 0) {

client[i] = connfd;/*儲存accept返回的檔案描述符到client[]*/

break;

}

}

}/*達到select能監控的檔案個數上限1024*/

if(i == FD_SETSIZE) {

fputs("to many clients\n",stderr);

exit(1);

}

/*新增一個新的檔案描述符到監控訊號集裡*/

FD_SET(connfd,&allset);

if(connfd > maxfd)

maxfd = connfd; /*select第一個引數需要*/

if(i > maxi)

maxi = i;/*更新client[]最大下標值*/

if(--nready == 0)

/*如果沒有更多的就緒檔案描述符,繼續回到上面select

* 負責處理未完成的就緒檔案描述符*/

continue;

}

for(i = 0;i< maxi;i++) {

if((sockfd = client[i]) < 0)

continue;

if(FD_ISSET(sockfd,&rset)) {

if((n=Read(sockfd,buf,MAXLINE)) == 0) {

/*client關閉連結時,伺服器端也關閉連結*/

Close(sockfd);

/*解除select監控檔案描述符*/

FD_CLR(sockfd,&allset);

client[i] = -1;

} else {

int j;

for(j = 0; j< n;j++) {

buf[j] = toupper(buf[j]);

}

Write(sockfd,buf,n);

}

if(--nready == 0)

break;

}

}

Close(listenfd);

return 0;

}

Client.c

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include"wrap.h"

#define MAXLINE 80

#define SERV_PORT 8000

int main(void) {

struct sockaddr_in servaddr;

char buf[MAXLINE];

int sockfd,n;

//1.Socket

sockfd = Socket(AF_INET,SOCK_STREAM,0);

//設定ip和埠號等相關資訊

bzero(&servaddr,sizeof(servaddr));

servaddr.sin_family = AF_INET;

inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);

servaddr.sin_port = htons(SERV_PORT);

//2.建立連線

Connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));

while(fgets(buf,MAXLINE,stdin) != NULL) {

//3.Write資料

Write(sockfd,buf,strlen(buf));

n = Read(sockfd,buf,MAXLINE);

if(n == 0) {

printf("the other side has been closed.\n");

} else {

Write(STDOUT_FILENO,buf,n);

}

}

Close(sockfd);

return 0;

}

Wrap.h

#ifndef __WRAP_H_

#define __WRAP_H_

void perr_exit(const char *s);

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);

void Bind(int fd, const struct sockaddr *sa, socklen_t salen);

void Connect(int fd, const struct sockaddr *sa, socklen_t salen);

void Listen(int fd, int backlog);

int Socket(int family, int type, int protocol);

ssize_t Read(int fd, void *ptr, size_t nbytes);

ssize_t Write(int fd, const void *ptr, size_t nbytes);

void Close(int fd);

ssize_t Readn(int fd, void *vptr, size_t n);

ssize_t Writen(int fd, const void *vptr, size_t n);

static ssize_t my_read(int fd, char *ptr);

ssize_t Readline(int fd, void *vptr, size_t maxlen);

#endif

Wrap.c

#include <stdlib.h>

#include <errno.h>

#include <sys/socket.h>

void perr_exit(const char *s)

{

perror(s);

exit(1);

}

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)

{

int n;

again:

if ( (n = accept(fd, sa, salenptr)) < 0) {

if ((errno == ECONNABORTED) || (errno == EINTR))

goto again;

else

perr_exit("accept error");

}

return n;

}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen)

{

if (bind(fd, sa, salen) < 0)

perr_exit("bind error");

}

void Connect(int fd, const struct sockaddr *sa, socklen_t salen)

{

if (connect(fd, sa, salen) < 0)

perr_exit("connect error");

}

void Listen(int fd, int backlog)

{

if (listen(fd, backlog) < 0)

perr_exit("listen error");

}

int Socket(int family, int type, int protocol)

{

int n;

if ( (n = socket(family, type, protocol)) < 0)

perr_exit("socket error");

return n;

}

ssize_t Read(int fd, void *ptr, size_t nbytes)

{

ssize_t n;

again:

if ( (n = read(fd, ptr, nbytes)) == -1) {

if (errno == EINTR)

goto again;

else

return -1;

}

return n;

}

ssize_t Write(int fd, const void *ptr, size_t nbytes)

{

ssize_t n;

again:

if ( (n = write(fd, ptr, nbytes)) == -1) {

if (errno == EINTR)

goto again;

else

return -1;

}

return n;

}

void Close(int fd)

{

if (close(fd) == -1)

perr_exit("close error");

}

ssize_t Readn(int fd, void *vptr, size_t n)

{

size_tnleft;

ssize_t nread;

char*ptr;

ptr = vptr;

nleft = n;

while (nleft > 0) {

if ( (nread = read(fd, ptr, nleft)) < 0) {

if (errno == EINTR)

nread = 0;

else

return -1;

} else if (nread == 0)

break;

nleft -= nread;

ptr += nread;

}

return n - nleft;

}

ssize_t Writen(int fd, const void *vptr, size_t n)

{

size_t nleft;

ssize_t nwritten;

const char *ptr;

ptr = vptr;

nleft = n;

while (nleft > 0) {

if ( (nwritten = write(fd, ptr, nleft)) <= 0) {

if (nwritten < 0 && errno == EINTR)

nwritten = 0;

else

return -1;

}

nleft -= nwritten;

ptr += nwritten;

}

return n;

}

static ssize_t my_read(int fd, char *ptr)

{

static int read_cnt;

static char *read_ptr;

static char read_buf[100];

if (read_cnt <= 0) {

again:

if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {

if (errno == EINTR)

goto again;

return -1;

} else if (read_cnt == 0)

return 0;

read_ptr = read_buf;

}

read_cnt--;

*ptr = *read_ptr++;

return 1;

}

ssize_t Readline(int fd, void *vptr, size_t maxlen)

{

ssize_t n, rc;

charc, *ptr;

ptr = vptr;

for (n = 1; n < maxlen; n++) {

if ( (rc = my_read(fd, &c)) == 1) {

*ptr++ = c;

if (c== '\n')

break;

} else if (rc == 0) {

*ptr = 0;

return n - 1;

} else

return -1;

}

*ptr= 0;

return n;

}