網路程式設計中I/O複用select的用法
阿新 • • 發佈:2018-12-23
網路程式設計select的用法
注:本文對select函式、相關引數及結構體不做解釋
select使用流程圖
注:沒有寫socket流程
在網路程式設計中需要新增的程式碼行以及意義
1.定義select相關變數
int listenfd,connfd,i;
/*套接字描述符/連結描述符(錄入環節)/變數*/
int nread,maxfd,maxi,sockfd;
/*就緒數/最大連結描述符/用於記錄連結描述符陣列當前最大使用的下標,減少無用的遍歷/
連結描述符(區別在於此處用於I/O複用的遍歷環節)*/
int client[FD_SETSIZE];
/*存放錄入的連結描述符*/
fd_set allest,rset;
/*檔案集合描述符:所有集合/就緒可讀集合*/
2.select相關變數的相關資料初始化
maxfd = listenfd;
/*初始化最大連結描述符,*/
maxi = -1;
for (i = 0; i < FD_SETSIZE; i++) client[i] = -1;
FD_ZERO(&allest);
/*清空allset描述符集*/
FD_SET(listenfd, &allest);
/*將監聽描述符加到allset中*/
3.select 監視可讀事件
rset = allest;
nread = select(maxfd+1, &rset, NULL, NULL, NULL);
/*呼叫select 監視可讀事件*/
4.新的連結描述符放入陣列和描述符集
if (FD_ISSET(listenfd, &rset))
/*判斷伺服器端套接字中是否有變化(是否有新的連結)*/
{
cliaddr_lin = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, & cliaddr_lin);
for (i = 0; i < FD_SETSIZE; i++)
{
if (client[i] < 0)
{
client[i] = connfd;
/ /*新的連結描述符放入陣列*/
break;
}
}
FD_SET(connfd, &allest);
/* 將來自客戶的連線connfd加入描述符集 */
if (connfd > maxfd) maxfd = connfd;
/*新的連結描述符*/
if (i > maxi) maxi = i;
if (--nread == 0) continue;
}
5.批處理可讀連結描述符
for (i = 0; i <= maxi; i++)
{
if ((sockfd = client[i]) < 0) continue;
if (FD_ISSET(sockfd, &rset))
{
if ((n = read (sockfd, buf, MAXLIN)) == 0)
{
close(sockfd);
FD_CLR(sockfd, &allest);
client[i] = -1;
}else
{
if (n < 0) perror("read:");
if (n > 0) write(sockfd, buf, n);
}
if (--nread == 0) break;
}
}
例程
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define LINK_NUMBER 20
#define SERV_PORT 8000
#define MAXLIN 100
int main(int argc, char const *argv[])
{
int listenfd,connfd,i;
int nread,maxfd,maxi,sockfd;
int client[FD_SETSIZE];
ssize_t n;
fd_set allest,rset;
socklen_t cliaddr_lin;
struct sockaddr_in servaddr,cliaddr;
char buf[MAXLIN],str[INET_ADDRSTRLEN];
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);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd,LINK_NUMBER);
maxfd = listenfd;
maxi = -1;
for (i = 0; i < FD_SETSIZE; i++) client[i] = -1;
FD_ZERO(&allest);
FD_SET(listenfd, &allest);
while(1)
{
rset = allest;
nread = select(maxfd+1, &rset, NULL, NULL, NULL);
if (nread < 0)
{
perror("select error:");
break;
}
if (FD_ISSET(listenfd, &rset))
{
cliaddr_lin = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_lin);
printf("received from %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
for (i = 0; i < FD_SETSIZE; i++)
{
if (client[i] < 0)
{
client[i] = connfd;
break;
}
}
if (i == FD_SETSIZE)
{
perror("too many clients");
exit(1);
}
FD_SET(connfd, &allest);
if (connfd > maxfd) maxfd = connfd;
if (i > maxi) maxi = i;
if (--nread == 0) continue;
}
for (i = 0; i <= maxi; i++)
{
if ((sockfd = client[i]) < 0) continue;
if (FD_ISSET(sockfd, &rset))
{
if ((n = read (sockfd, buf, MAXLIN)) == 0)
{
close(sockfd);
FD_CLR(sockfd, &allest);
client[i] = -1;
}else
{
if (n < 0)
{
perror("read:");
}
if (n > 0)
{
write(sockfd, buf, n);
}
}
if (--nread == 0) break;
}
}
/*socket*/
/*while(1)
{
if((n = read(connfd,buf,MAXLIN)) <= 0)
{
printf("err" );
break;
}
printf("received from %s at PORT %d::%s\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port),buf);
write(connfd,buf,n);
}
close(connfd);*/
}
return 0;
}
參考文獻及部落格
Linux聊天室專案 – ChatRome(select實現)
Linux I/O複用之select函式詳解
unix網路程式設計第二版