日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时

發布時間:2025/5/22 linux 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、使用alarm 函數設置超時

?

C++ Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
? void?handler( int?sig)
{
}
signal(SIGALRM,?handler);

alarm( 5);
int?ret?=?read(fd,?buf,? sizeof(buf));
if?(ret?==?- 1?&&?errno?==?EINTR)
????errno?=?ETIMEOUT;
else? if?(ret?>=? 0)
????alarm( 0);
.................

?

?

程序大概框架如上所示,如果read在5s內被SIGALRM信號中斷而返回,則表示超時,否則未超時已讀取到數據,取消鬧鐘。但這種方法不常用,因為有時可能在其他地方使用了alarm會造成混亂。


二、使用套接字選項SO_SNDTIMEO、SO_RCVTIMEO

?

C++ Code?
1
2
3
4
5
6
? setsockopt(sock,?SOL_SOCKET,?SO_RCVTIMEO,? 5);

int?ret?=?read(sock,?buf,? sizeof(buf));
if?(ret?==?- 1?&&?errno?==?EWOULDBLOCK)
????errno?=?ETIMEOUT;
..........

?

?

即使用setsockopt 函數進行設置,但這種方法可移植性比較差,不是每種系統實現都有這些選項。


三、使用select 實現超時

下面程序包含read_timeout、write_timeout、accept_timeout、connect_timeout 四個函數封裝

?

C++ Code?
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
228
229
230
231
232
233
234
235
236
237
? /*************************************************************************
????>?File?Name:?sysutil.c
????>?Author:?Simba
????>?Mail:?dameng34@163.com
????>?Created?Time:?Sat?02?Mar?2013?10:53:06?PM?CST
?************************************************************************/


#include? "sysutil.h"

/*?read_timeout?-?讀超時檢測函數,不含讀操作
?*?fd:文件描述符
?*?wait_seconds:等待超時秒數,?如果為0表示不檢測超時;
?*?成功(未超時)返回0,失敗返回-1,超時返回-1并且errno?=?ETIMEDOUT
?*/


int?read_timeout( int?fd,? unsigned? int?wait_seconds)
{
???? int?ret?=? 0;
???? if?(wait_seconds?>? 0)
????{

????????fd_set?read_fdset;
???????? struct?timeval?timeout;

????????FD_ZERO(&read_fdset);
????????FD_SET(fd,?&read_fdset);

????????timeout.tv_sec?=?wait_seconds;
????????timeout.tv_usec?=? 0;

???????? do
????????{
????????????ret?=?select(fd?+? 1,?&read_fdset,? NULL,? NULL,?&timeout);? //select會阻塞直到檢測到事件或者超時
???????????? //?如果select檢測到可讀事件發送,則此時調用read不會阻塞
????????}
???????? while?(ret?<? 0?&&?errno?==?EINTR);

???????? if?(ret?==? 0)
????????{
????????????ret?=?- 1;
????????????errno?=?ETIMEDOUT;
????????}
???????? else? if?(ret?==? 1)
???????????? return? 0;

????}

???? return?ret;
}

/*?write_timeout?-?寫超時檢測函數,不含寫操作
?*?fd:文件描述符
?*?wait_seconds:等待超時秒數,?如果為0表示不檢測超時;
?*?成功(未超時)返回0,失敗返回-1,超時返回-1并且errno?=?ETIMEDOUT
?*/


int?write_timeout( int?fd,? unsigned? int?wait_seconds)
{
???? int?ret?=? 0;
???? if?(wait_seconds?>? 0)
????{

????????fd_set?write_fdset;
???????? struct?timeval?timeout;

????????FD_ZERO(&write_fdset);
????????FD_SET(fd,?&write_fdset);

????????timeout.tv_sec?=?wait_seconds;
????????timeout.tv_usec?=? 0;

???????? do
????????{
????????????ret?=?select(fd?+? 1,? NULL,?&write_fdset,? NULL,?&timeout);
????????}
???????? while?(ret?<? 0?&&?errno?==?EINTR);

???????? if?(ret?==? 0)
????????{
????????????ret?=?- 1;
????????????errno?=?ETIMEDOUT;
????????}
???????? else? if?(ret?==? 1)
???????????? return? 0;

????}

???? return?ret;
}

/*?accept_timeout?-?帶超時的accept
?*?fd:?套接字
?*?addr:?輸出參數,返回對方地址
?*?wait_seconds:?等待超時秒數,如果為0表示正常模式
?*?成功(未超時)返回已連接套接字,失敗返回-1,超時返回-1并且errno?=?ETIMEDOUT
?*/


int?accept_timeout( int?fd,? struct?sockaddr_in?*addr,? unsigned? int?wait_seconds)
{
???? int?ret;
????socklen_t?addrlen?=? sizeof( struct?sockaddr_in);

???? if?(wait_seconds?>? 0)
????{

????????fd_set?accept_fdset;
???????? struct?timeval?timeout;
????????FD_ZERO(&accept_fdset);
????????FD_SET(fd,?&accept_fdset);

????????timeout.tv_sec?=?wait_seconds;
????????timeout.tv_usec?=? 0;

???????? do
????????{
????????????ret?=?select(fd?+? 1,?&accept_fdset,? NULL,? NULL,?&timeout);
????????}
???????? while?(ret?<? 0?&&?errno?==?EINTR);

???????? if?(ret?==?- 1)
???????????? return?- 1;
???????? else? if?(ret?==? 0)
????????{
????????????errno?=?ETIMEDOUT;
???????????? return?- 1;
????????}
????}

???? if?(addr?!=? NULL)
????????ret?=?accept(fd,?( struct?sockaddr?*)addr,?&addrlen);
???? else
????????ret?=?accept(fd,? NULL,? NULL);
???? if?(ret?==?- 1)
????????ERR_EXIT( "accpet?error");

???? return?ret;
}

/*?activate_nonblock?-?設置IO為非阻塞模式
?*?fd:?文件描述符
?*/

void?activate_nonblock( int?fd)
{
???? int?ret;
???? int?flags?=?fcntl(fd,?F_GETFL);
???? if?(flags?==?- 1)
????????ERR_EXIT( "fcntl?error");

????flags?|=?O_NONBLOCK;
????ret?=?fcntl(fd,?F_SETFL,?flags);
???? if?(ret?==?- 1)
????????ERR_EXIT( "fcntl?error");
}

/*?deactivate_nonblock?-?設置IO為阻塞模式
?*?fd:?文件描述符
?*/

void?deactivate_nonblock( int?fd)
{
???? int?ret;
???? int?flags?=?fcntl(fd,?F_GETFL);
???? if?(flags?==?- 1)
????????ERR_EXIT( "fcntl?error");

????flags?&=?~O_NONBLOCK;
????ret?=?fcntl(fd,?F_SETFL,?flags);
???? if?(ret?==?- 1)
????????ERR_EXIT( "fcntl?error");
}

/*?connect_timeout?-?帶超時的connect
?*?fd:?套接字
?*?addr:?輸出參數,返回對方地址
?*?wait_seconds:?等待超時秒數,如果為0表示正常模式
?*?成功(未超時)返回0,失敗返回-1,超時返回-1并且errno?=?ETIMEDOUT
?*/

int?connect_timeout( int?fd,? struct?sockaddr_in?*addr,? unsigned? int?wait_seconds)
{
???? int?ret;
????socklen_t?addrlen?=? sizeof( struct?sockaddr_in);

???? if?(wait_seconds?>? 0)
????????activate_nonblock(fd);

????ret?=?connect(fd,?( struct?sockaddr?*)addr,?addrlen);
???? if?(ret?<? 0?&&?errno?==?EINPROGRESS)
????{

????????fd_set?connect_fdset;
???????? struct?timeval?timeout;
????????FD_ZERO(&connect_fdset);
????????FD_SET(fd,?&connect_fdset);

????????timeout.tv_sec?=?wait_seconds;
????????timeout.tv_usec?=? 0;

???????? do
????????{
???????????? /*?一旦連接建立,套接字就可寫?*/
????????????ret?=?select(fd?+? 1,? NULL,?&connect_fdset,? NULL,?&timeout);
????????}
???????? while?(ret?<? 0?&&?errno?==?EINTR);

???????? if?(ret?==? 0)
????????{
????????????errno?=?ETIMEDOUT;
???????????? return?- 1;
????????}
???????? else? if?(ret?<? 0)
???????????? return?- 1;

???????? else? if?(ret?==? 1)
????????{
???????????? /*?ret返回為1,可能有兩種情況,一種是連接建立成功,一種是套接字產生錯誤
?????????????*?此時錯誤信息不會保存至errno變量中(select沒出錯),因此,需要調用
?????????????*?getsockopt來獲取?*/

???????????? int?err;
????????????socklen_t?socklen?=? sizeof(err);
???????????? int?sockoptret?=?getsockopt(fd,?SOL_SOCKET,?SO_ERROR,?&err,?&socklen);
???????????? if?(sockoptret?==?- 1)
???????????????? return?- 1;
???????????? if?(err?==? 0)
????????????????ret?=? 0;
???????????? else
????????????{
????????????????errno?=?err;
????????????????ret?=?- 1;
????????????}
????????}
????}

???? if?(wait_seconds?>? 0)
????????deactivate_nonblock(fd);


???? return?ret;
}
下面來解析一下這些函數的封裝:
1、read_timeout :如注釋所寫,這只是讀超時檢測函數,并不包含讀操作,如果從此函數成功返回,則此時調用read將不再阻塞,測試代碼可以這樣寫: C++ Code?
1
2
3
4
5
6
7
8
? int?ret;
ret?=?read_timeout(fd,? 5);
if?(ret?==? 0)
????read(fd,?buf,? sizeof(buf));
else? if?(ret?==?- 1?&&?errno?==?ETIMEOUT)
????printf( "timeout...\n");
else
????ERR_EXIT( "read_timeout");

?

如果 read_timeout(fd, 0); 則表示不檢測超時,函數直接返回為0,此時再調用read 將會阻塞。

當wait_seconds 參數大于0,則進入if 括號執行,將超時時間設置為select函數的超時時間結構體,select會阻塞直到檢測到事件發生或者超時。如果select返回-1且errno 為EINTR,說明是被信號中斷,需要重啟select;如果select返回0表示超時;如果select返回1表示檢測到可讀事件;否則select返回-1 表示出錯。


2、write_timeout :此函數跟read_timeout 函數類似,只是select 關心的是可寫事件,不再贅述。


3、accept_timeout :此函數是帶超時的accept 函數,如果能從if (wait_seconds > 0) 括號執行后向下執行,說明select 返回為1,檢測到已連接隊列不為空,此時再調用accept 不再阻塞,當然如果wait_seconds == 0 則像正常模式一樣,accept 阻塞等待,注意,accept 返回的是已連接套接字。


4、connect_timeout :在調用connect前需要使用fcntl 函數將套接字標志設置為非阻塞,如果網絡環境很好,則connect立即返回0,不進入if 大括號執行;如果網絡環境擁塞,則connect返回-1且errno == EINPROGRESS,表示正在處理。此后調用select與前面3個函數類似,但這里關注的是可寫事件,因為一旦連接建立,套接字就可寫。還需要注意的是當select 返回1,可能有兩種情況,一種是連接成功,一種是套接字產生錯誤,由這里可知,這兩種情況都會產生可寫事件,所以需要使用getsockopt來獲取一下。退出之前還需重新將套接字設置為阻塞。


我們可以寫個小程序測試一下connect_timeout 函數,客戶端程序如下:

?

C++ Code?
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
? #include? "sysutil.h"

int?main( void)
{
???? int?sock;
???? if?((sock?=?socket(PF_INET,?SOCK_STREAM,?IPPROTO_TCP))?<? 0)
????????ERR_EXIT( "socket");

???? struct?sockaddr_in?servaddr;
????memset(&servaddr,? 0,? sizeof(servaddr));
????servaddr.sin_family?=?AF_INET;
????servaddr.sin_port?=?htons( 5188);
????servaddr.sin_addr.s_addr?=?inet_addr( "127.0.0.1");


???? int?ret?=?connect_timeout(sock,?&servaddr,? 5);
???? if?(ret?==?- 1?&&?errno?==?ETIMEDOUT)
????{
????????printf( "timeout...\n");
???????? return? 1;
????}
???? else? if?(ret?==?- 1)
????????ERR_EXIT( "connect_timeout");

???? struct?sockaddr_in?localaddr;
????socklen_t?addrlen?=? sizeof(localaddr);
???? if?(getsockname(sock,?( struct?sockaddr?*)&localaddr,?&addrlen)?<? 0)
????????ERR_EXIT( "getsockname");

????printf( "ip=%s?port=%d\n",?inet_ntoa(localaddr.sin_addr),?ntohs(localaddr.sin_port));


???? return? 0;
}

?

?

因為是在本機上測試,所以不會出現超時的情況,但出錯的情況還是可以看到的,比如不要啟動服務器端程序,而直接啟動客戶端程序,輸出如下:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echocli_timeout?
connect_timeout: Connection refused

很明顯是connect_timeout 函數返回了-1,我們也可以推算出connect_timeout 函數中,select返回1,但卻是套接字發生錯誤的情況,errno = ECONNREFUSED,所以打印出Connection refused。


?

參考:

《Linux C 編程一站式學習》

《TCP/IP詳解 卷一》

《UNP》

轉載于:https://www.cnblogs.com/snake-hand/archive/2013/06/11/3132321.html

總結

以上是生活随笔為你收集整理的linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲第一综合 | 欧美日韩综合一区二区三区 | 丁香花高清在线观看完整动漫 | a在线免费| 96av在线| 成人动漫在线免费观看 | 亚洲欧美综合自拍 | 欧美亚洲图片小说 | 91成人免费电影 | 亚洲免费影院 | 名人明星三级videos | 91视频在线观看网站 | 亚洲 欧美 日韩 国产综合 在线 | 一区二区三区欧美视频 | 麻豆网页 | 一区视频网站 | 国产一二三视频 | 日韩精品久久久久久 | 日本黄色美女视频 | 亚洲精品久久久蜜桃网尤妮丝 | 久久国产网站 | 一起艹在线观看 | 18禁超污无遮挡无码免费游戏 | 在线波多野结衣 | 午夜8888| 久久久久人妻一区二区三区 | 亚洲精品www久久久久久 | 午夜精品导航 | 徐锦江一级淫片免费看 | 成人午夜淫片免费观看 | av高清在线| 天天干天天爽天天射 | 伊人9 | 亚洲熟妇无码乱子av电影 | 中日毛片 | 无码人妻丰满熟妇区毛片18 | 日韩毛片在线播放 | 日本人妻不卡一区二区三区中文字幕 | 欧美亚洲一区二区三区四区 | 麻豆啪啪 | 69成人网| 嫩草午夜少妇在线影视 | 欧美一二三区视频 | 亚洲天堂av免费在线观看 | 午夜精品国产 | 欧美日韩成人在线观看 | 亚洲成人精 | 欧美日韩精品一区 | 国产黄色小视频在线观看 | 调教在线观看 | 另类色综合| 国产精品高潮呻吟久久aⅴ码 | 噜噜狠狠狠狠综合久久 | 久久77777| 久久九精品 | 亚洲天堂网在线观看视频 | 韩日av | 久久在线中文字幕 | 国产九九九九 | 欧美大片一级 | 女人18毛片毛片毛片毛片区二 | 精品人妻一区二区免费视频 | 久久国产精品99久久人人澡 | 久久性生活视频 | 自拍偷拍国内 | av白浆 | 午夜精品久久久久久久无码 | 校园春色综合网 | 蕾丝视频污 | 中日韩在线观看 | 久久精品国产亚洲av麻豆蜜芽 | 美女被草网站 | 久久免费黄色网址 | 日韩黄色一级大片 | 日韩视频一二三区 | 久久伊人影院 | 日本高清有码视频 | 亚洲h视频在线观看 | 欧美丰满美乳xxx高潮www | 亚洲黄色精品 | 日本涩涩网 | 欧美黄色大片免费观看 | 亚洲免费三级 | 播放男人添女人下边视频 | 人妻 日韩 欧美 综合 制服 | 婷婷五月综合缴情在线视频 | 国产成人精品视频一区二区 | www.日本黄 | 日日精| 成人免费视频一区二区 | 秋霞成人网 | 久久久久看片 | 久久精品国产亚洲AV高清综合 | 一区二区三区免费观看视频 | xxx黄色片 | 日本 奴役 捆绑 受虐狂xxxx | 欧美久久一区 | 性高潮久久久久久久 | 免费a网址 |