一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

Linux|Centos|Ubuntu|系統(tǒng)進程|Fedora|注冊表|Bios|Solaris|Windows7|Windows10|Windows11|windows server|

服務(wù)器之家 - 服務(wù)器系統(tǒng) - Linux - IO多路復(fù)用之select全面總結(jié)(必看篇)

IO多路復(fù)用之select全面總結(jié)(必看篇)

2021-12-13 20:59Linux教程網(wǎng) Linux

下面小編就為大家?guī)硪黄狪O多路復(fù)用之select全面總結(jié)(必看篇)。小編覺得挺不錯的?,F(xiàn)在就分享給大家。也給大家做個參考。一起跟隨小編過來看看吧

1、基本概念

io多路復(fù)用是指內(nèi)核一旦發(fā)現(xiàn)進程指定的一個或者多個io條件準(zhǔn)備讀取,它就通知該進程。io多路復(fù)用適用如下場合:

(1)當(dāng)客戶處理多個描述字時(一般是交互式輸入和網(wǎng)絡(luò)套接口),必須使用i/o復(fù)用。

(2)當(dāng)一個客戶同時處理多個套接口時,而這種情況是可能的,但很少出現(xiàn)。

(3)如果一個tcp服務(wù)器既要處理監(jiān)聽套接口,又要處理已連接套接口,一般也要用到i/o復(fù)用。

(4)如果一個服務(wù)器即要處理tcp,又要處理udp,一般要使用i/o復(fù)用。

(5)如果一個服務(wù)器要處理多個服務(wù)或多個協(xié)議,一般要使用i/o復(fù)用。

與多進程和多線程技術(shù)相比,i/o多路復(fù)用技術(shù)的最大優(yōu)勢是系統(tǒng)開銷小,系統(tǒng)不必創(chuàng)建進程/線程,也不必維護這些進程/線程,從而大大減小了系統(tǒng)的開銷。

2、select函數(shù)

該函數(shù)準(zhǔn)許進程指示內(nèi)核等待多個事件中的任何一個發(fā)送,并只在有一個或多個事件發(fā)生或經(jīng)歷一段指定的時間后才喚醒。函數(shù)原型如下:

#include <sys/select.h>
#include <sys/time.h>

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout)

返回值:就緒描述符的數(shù)目,超時返回0,出錯返回-1

函數(shù)參數(shù)介紹如下:

(1)第一個參數(shù)maxfdp1指定待測試的描述字個數(shù),它的值是待測試的最大描述字加1(因此把該參數(shù)命名為maxfdp1),描述字0、1、2...maxfdp1-1均將被測試。

因為文件描述符是從0開始的。

(2)中間的三個參數(shù)readset、writeset和exceptset指定我們要讓內(nèi)核測試讀、寫和異常條件的描述字。如果對某一個的條件不感興趣,就可以把它設(shè)為空指針。struct fd_set可以理解為一個集合,這個集合中存放的是文件描述符,可通過以下四個宏進行設(shè)置:

void fd_zero(fd_set *fdset);           //清空集合

void fd_set(int fd, fd_set *fdset);   //將一個給定的文件描述符加入集合之中

void fd_clr(int fd, fd_set *fdset);   //將一個給定的文件描述符從集合中刪除

int fd_isset(int fd, fd_set *fdset);   // 檢查集合中指定的文件描述符是否可以讀寫

(3)timeout告知內(nèi)核等待所指定描述字中的任何一個就緒可花多少時間。其timeval結(jié)構(gòu)用于指定這段時間的秒數(shù)和微秒數(shù)。

?
1
2
3
4
5
6
7
struct timeval{
 
      long tv_sec;  //seconds
 
      long tv_usec; //microseconds
 
};

這個參數(shù)有三種可能:

(1)永遠等待下去:僅在有一個描述字準(zhǔn)備好i/o時才返回。為此,把該參數(shù)設(shè)置為空指針null。

(2)等待一段固定時間:在有一個描述字準(zhǔn)備好i/o時返回,但是不超過由該參數(shù)所指向的timeval結(jié)構(gòu)中指定的秒數(shù)和微秒數(shù)。

(3)根本不等待:檢查描述字后立即返回,這稱為輪詢。為此,該參數(shù)必須指向一個timeval結(jié)構(gòu),而且其中的定時器值必須為0。

 原理圖:

IO多路復(fù)用之select全面總結(jié)(必看篇)

3、測試程序

寫一個tcp回射程序,程序的功能是:客戶端向服務(wù)器發(fā)送信息,服務(wù)器接收并原樣發(fā)送給客戶端,客戶端顯示出接收到的信息。

服務(wù)端程序如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <assert.h>
 
#define ipaddr   "127.0.0.1"
#define port    8787
#define maxline   1024
#define listenq   5
#define size    10
 
typedef struct server_context_st
{
  int cli_cnt;    /*客戶端個數(shù)*/
  int clifds[size];  /*客戶端的個數(shù)*/
  fd_set allfds;   /*句柄集合*/
  int maxfd;     /*句柄最大值*/
} server_context_st;
static server_context_st *s_srv_ctx = null;
/*===========================================================================
 * ==========================================================================*/
static int create_server_proc(const char* ip,int port)
{
  int fd;
  struct sockaddr_in servaddr;
  fd = socket(af_inet, sock_stream,0);
  if (fd == -1) {
    fprintf(stderr, "create socket fail,erron:%d,reason:%s\n",
        errno, strerror(errno));
    return -1;
  }
 
  /*一個端口釋放后會等待兩分鐘之后才能再被使用,so_reuseaddr是讓端口釋放后立即就可以被再次使用。*/
  int reuse = 1;
  if (setsockopt(fd, sol_socket, so_reuseaddr, &reuse, sizeof(reuse)) == -1) {
    return -1;
  }
 
  bzero(&servaddr,sizeof(servaddr));
  servaddr.sin_family = af_inet;
  inet_pton(af_inet,ip,&servaddr.sin_addr);
  servaddr.sin_port = htons(port);
 
  if (bind(fd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1) {
    perror("bind error: ");
    return -1;
  }
 
  listen(fd,listenq);
 
  return fd;
}
 
static int accept_client_proc(int srvfd)
{
  struct sockaddr_in cliaddr;
  socklen_t cliaddrlen;
  cliaddrlen = sizeof(cliaddr);
  int clifd = -1;
 
  printf("accpet clint proc is called.\n");
 
accept:
  clifd = accept(srvfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
 
  if (clifd == -1) {
    if (errno == eintr) {
      goto accept;
    } else {
      fprintf(stderr, "accept fail,error:%s\n", strerror(errno));
      return -1;
    }
  }
 
  fprintf(stdout, "accept a new client: %s:%d\n",
      inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
 
  //將新的連接描述符添加到數(shù)組中
  int i = 0;
  for (i = 0; i < size; i++) {
    if (s_srv_ctx->clifds[i] < 0) {
      s_srv_ctx->clifds[i] = clifd;
      s_srv_ctx->cli_cnt++;
      break;
    }
  }
 
  if (i == size) {
    fprintf(stderr,"too many clients.\n");
    return -1;
  }
101 }
 
static int handle_client_msg(int fd, char *buf)
{
  assert(buf);
  printf("recv buf is :%s\n", buf);
  write(fd, buf, strlen(buf) +1);
  return 0;
}
 
static void recv_client_msg(fd_set *readfds)
{
  int i = 0, n = 0;
  int clifd;
  char buf[maxline] = {0};
  for (i = 0;i <= s_srv_ctx->cli_cnt;i++) {
    clifd = s_srv_ctx->clifds[i];
    if (clifd < 0) {
      continue;
    }
    /*判斷客戶端套接字是否有數(shù)據(jù)*/
    if (fd_isset(clifd, readfds)) {
      //接收客戶端發(fā)送的信息
      n = read(clifd, buf, maxline);
      if (n <= 0) {
        /*n==0表示讀取完成,客戶都關(guān)閉套接字*/
        fd_clr(clifd, &s_srv_ctx->allfds);
        close(clifd);
        s_srv_ctx->clifds[i] = -1;
        continue;
      }
      handle_client_msg(clifd, buf);
    }
  }
}
static void handle_client_proc(int srvfd)
{
  int clifd = -1;
  int retval = 0;
  fd_set *readfds = &s_srv_ctx->allfds;
  struct timeval tv;
  int i = 0;
 
  while (1) {
    /*每次調(diào)用select前都要重新設(shè)置文件描述符和時間,因為事件發(fā)生后,文件描述符和時間都被內(nèi)核修改啦*/
    fd_zero(readfds);
    /*添加監(jiān)聽套接字*/
    fd_set(srvfd, readfds);
    s_srv_ctx->maxfd = srvfd;
 
    tv.tv_sec = 30;
    tv.tv_usec = 0;
    /*添加客戶端套接字*/
    for (i = 0; i < s_srv_ctx->cli_cnt; i++) {
      clifd = s_srv_ctx->clifds[i];
      /*去除無效的客戶端句柄*/
      if (clifd != -1) {
        fd_set(clifd, readfds);
      }
      s_srv_ctx->maxfd = (clifd > s_srv_ctx->maxfd ? clifd : s_srv_ctx->maxfd);
    }
 
    /*開始輪詢接收處理服務(wù)端和客戶端套接字*/
    retval = select(s_srv_ctx->maxfd + 1, readfds, null, null, &tv);
    if (retval == -1) {
      fprintf(stderr, "select error:%s.\n", strerror(errno));
      return;
    }
    if (retval == 0) {
      fprintf(stdout, "select is timeout.\n");
      continue;
    }
    if (fd_isset(srvfd, readfds)) {
      /*監(jiān)聽客戶端請求*/
      accept_client_proc(srvfd);
    } else {
      /*接受處理客戶端消息*/
      recv_client_msg(readfds);
    }
  }
}
 
static void server_uninit()
{
  if (s_srv_ctx) {
    free(s_srv_ctx);
    s_srv_ctx = null;
  }
}
 
static int server_init()
{
  s_srv_ctx = (server_context_st *)malloc(sizeof(server_context_st));
  if (s_srv_ctx == null) {
    return -1;
  }
 
  memset(s_srv_ctx, 0, sizeof(server_context_st));
 
  int i = 0;
  for (;i < size; i++) {
    s_srv_ctx->clifds[i] = -1;
  }
 
  return 0;
}
 
int main(int argc,char *argv[])
{
  int srvfd;
  /*初始化服務(wù)端context*/
  if (server_init() < 0) {
    return -1;
  }
  /*創(chuàng)建服務(wù),開始監(jiān)聽客戶端請求*/
  srvfd = create_server_proc(ipaddr, port);
  if (srvfd < 0) {
    fprintf(stderr, "socket create or bind fail.\n");
    goto err;
  }
  /*開始接收并處理客戶端請求*/
  handle_client_proc(srvfd);
  server_uninit();
  return 0;
err:
  server_uninit();
  return -1;
}

客戶端程序如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
 
#define maxline 1024
#define ipaddress "127.0.0.1"
#define serv_port 8787
 
#define max(a,b) (a > b) ? a : b
 
static void handle_recv_msg(int sockfd, char *buf)
{
printf("client recv msg is:%s\n", buf);
sleep(5);
write(sockfd, buf, strlen(buf) +1);
}
 
static void handle_connection(int sockfd)
{
char sendline[maxline],recvline[maxline];
int maxfdp,stdineof;
fd_set readfds;
int n;
struct timeval tv;
int retval = 0;
 
while (1) {
 
fd_zero(&readfds);
fd_set(sockfd,&readfds);
maxfdp = sockfd;
 
tv.tv_sec = 5;
tv.tv_usec = 0;
 
retval = select(maxfdp+1,&readfds,null,null,&tv);
 
if (retval == -1) {
return ;
}
 
if (retval == 0) {
printf("client timeout.\n");
continue;
}
 
if (fd_isset(sockfd, &readfds)) {
n = read(sockfd,recvline,maxline);
if (n <= 0) {
fprintf(stderr,"client: server is closed.\n");
close(sockfd);
fd_clr(sockfd,&readfds);
return;
}
 
handle_recv_msg(sockfd, recvline);
}
}
}
 
int main(int argc,char *argv[])
{
int sockfd;
struct sockaddr_in servaddr;
 
sockfd = socket(af_inet,sock_stream,0);
 
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = af_inet;
servaddr.sin_port = htons(serv_port);
inet_pton(af_inet,ipaddress,&servaddr.sin_addr);
 
int retval = 0;
retval = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
if (retval < 0) {
fprintf(stderr, "connect fail,error:%s\n", strerror(errno));
return -1;
}
 
printf("client send to server .\n");
write(sockfd, "hello server", 32);
 
handle_connection(sockfd);
 
return 0;
}

4、程序結(jié)果

啟動服務(wù)程序,執(zhí)行三個個客戶程序進行測試,結(jié)果如下圖所示:

IO多路復(fù)用之select全面總結(jié)(必看篇)

以上就是小編為大家?guī)淼膇o多路復(fù)用之select全面總結(jié)(必看篇)全部內(nèi)容了,希望大家多多支持服務(wù)器之家~

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 双性鞭蒂软汁淋漓 | 果冻传媒天美传媒乌鸦传媒 | 日本天堂视频在线观看 | 午夜一级免费视频 | 色综合国产| 亚洲精品视频观看 | 色漫在线观看 | 关晓彤被调教出奶水的视频 | 暖暖免费高清完整版观看日本 | 激情三级做爰在线观看激情 | free性videoxxⅹ印度 | 亚洲精品久久久打桩机 | 国产肥臀 | 亚洲天堂日韩在线 | 日本免费v片一二三区 | 亚洲国产自 | 国产香蕉一区二区在线网站 | 性欧美videofree中文字幕 | 亚洲天堂色视频 | 亚洲精品6久久久久中文字幕 | 国产高清ujzzujzz| 免看一级a一片成人123 | 国产精品免费aⅴ片在线观看 | 久久精品国产亚洲AV热无遮挡 | 催眠白丝舞蹈老师小说 | 色婷婷久久综合中文久久一本 | 男人的天堂在线观看视频不卡 | 好紧好爽范冰冰系列 | 亚洲国产成人久久精品hezyo | hd性欧美俱乐部中文 | 恩爱夫妇交换小说 | 88av免费观看 | 毛片在线免费视频 | 午夜一级| 第一次出血videos | 3d动漫被吸乳羞羞 | 国产综合视频 | chinese野外gay军人 | 国产精品久久毛片蜜月 | 非洲黑人又大粗gay 非洲黑人bbwbbwbbw | 亚洲一区二区三区福利在线 |