博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
socket编程的同步、异步与阻塞、非阻塞示例详解
阅读量:5031 次
发布时间:2019-06-12

本文共 14210 字,大约阅读时间需要 47 分钟。

   

分类: 架构设计与优化

 

简介
图 1. 基本 Linux I/O 模型的简单矩阵
基本 Linux I/O 模型的简单矩阵 
每个 I/O 模型都有自己的使用模式,它们对于特定的应用程序都有自己的优点。
本节将简要对其一一进行介绍。
一、同步阻塞模式
在这个模式中,用户空间的应用程序执行一个系统调用,并阻塞,直到系统调用完成为止(数据传输完成或发生错误)。
/* * \brief * tcp client */#include 
#include
#include
#include
#include
#define SERVPORT 8080#define MAXDATASIZE 100int main(int argc, char *argv[]){ int sockfd, recvbytes; char rcv_buf[MAXDATASIZE]; /*./client 127.0.0.1 hello */ char snd_buf[MAXDATASIZE]; struct hostent *host; /* struct hostent * { * char *h_name; // general hostname * char **h_aliases; // hostname's alias * int h_addrtype; // AF_INET * int h_length; * char **h_addr_list; * }; */ struct sockaddr_in server_addr; if (argc < 3) { printf("Usage:%s [ip address] [any string]\n", argv[0]); return 1; } *snd_buf = '\0'; strcat(snd_buf, argv[2]); if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket:"); exit(1); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVPORT); inet_pton(AF_INET, argv[1], &server_addr.sin_addr); memset(&(server_addr.sin_zero), 0, 8); /* create the connection by socket * means that connect "sockfd" to "server_addr" * 同步阻塞模式 */ if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); exit(1); } /* 同步阻塞模式 */ if (send(sockfd, snd_buf, sizeof(snd_buf), 0) == -1) { perror("send:"); exit(1); } printf("send:%s\n", snd_buf); /* 同步阻塞模式 */ if ((recvbytes = recv(sockfd, rcv_buf, MAXDATASIZE, 0)) == -1) { perror("recv:"); exit(1); } rcv_buf[recvbytes] = '\0'; printf("recv:%s\n", rcv_buf); close(sockfd); return 0;}

 

显然,代码中的connect, send, recv都是同步阻塞工作模式,
在结果没有返回时,程序什么也不做,
二、同步非阻塞模式
同步阻塞 I/O 的一种效率稍低的变种是同步非阻塞 I/O。
在这种模型中,系统调用是以非阻塞的形式打开的。
这意味着 I/O 操作不会立即完成, 操作可能会返回一个错误代码,
说明这个命令不能立即满足(EAGAIN 或 EWOULDBLOCK),
非阻塞的实现是 I/O 命令可能并不会立即满足,需要应用程序调用许多次来等待操作完成。
这可能效率不高,
因为在很多情况下,当内核执行这个命令时,应用程序必须要进行忙碌等待,直到数据可用为止,
或者试图执行其他工作。
因为数据在内核中变为可用到用户调用 read 返回数据之间存在一定的间隔,这会导致整体数据吞吐量的降低。
/* * \brief * tcp client */#include 
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVPORT 8080#define MAXDATASIZE 100int main(int argc, char *argv[]){ int sockfd, recvbytes; char rcv_buf[MAXDATASIZE]; /*./client 127.0.0.1 hello */ char snd_buf[MAXDATASIZE]; struct hostent *host; /* struct hostent * { * char *h_name; // general hostname * char **h_aliases; // hostname's alias * int h_addrtype; // AF_INET * int h_length; * char **h_addr_list; * }; */ struct sockaddr_in server_addr; int flags; int addr_len; if (argc < 3) { printf("Usage:%s [ip address] [any string]\n", argv[0]); return 1; } *snd_buf = '\0'; strcat(snd_buf, argv[2]); if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket:"); exit(1); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVPORT); inet_pton(AF_INET, argv[1], &server_addr.sin_addr); memset(&(server_addr.sin_zero), 0, 8); addr_len = sizeof(struct sockaddr_in); /* Setting socket to nonblock */ flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, flags|O_NONBLOCK); /* create the connection by socket * means that connect "sockfd" to "server_addr" * 同步阻塞模式 */ if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); exit(1); } /* 同步非阻塞模式 */ while (send(sockfd, snd_buf, sizeof(snd_buf), MSG_DONTWAIT) == -1) { sleep(1); printf("sleep\n"); } printf("send:%s\n", snd_buf); /* 同步非阻塞模式 */ while ((recvbytes = recv(sockfd, rcv_buf, MAXDATASIZE, MSG_DONTWAIT)) == -1) { sleep(1); printf("sleep\n"); } rcv_buf[recvbytes] = '\0'; printf("recv:%s\n", rcv_buf); close(sockfd); return 0;}

 

异步阻塞模式,异步非阻塞模式以及server端程序见本文的第二部分。
http://blog.chinaunix.net/uid-26000296-id-3755268.html
 
1 三、异步阻塞模式  2 另外一个阻塞解决方案是带有阻塞通知的非阻塞 I/O。  3 在这种模型中,配置的是非阻塞 I/O,然后使用阻塞 select 系统调用来确定一个 I/O 描述符何时有操作。  4 使 select 调用非常有趣的是它可以用来为多个描述符提供通知,而不仅仅为一个描述符提供通知。  5 对于每个提示符来说,我们可以请求这个描述符可以写数据、有读数据可用以及是否发生错误的通知  6   7 下面的C语言实现的例子,它从网络上接受数据写入一个文件中:  8 /*  9  * \brief 10  * tcp client 11  */ 12  13 #include 
14 #include
15 #include
16 #include
17 #include
18 #include
19 #include
20 21 #include
22 #include
23 #include
24 #define SERVPORT 8080 25 #define MAXDATASIZE 100 26 #define TFILE "data_from_socket.txt" 27 28 29 int main(int argc, char *argv[]) 30 { 31 int sockfd, recvbytes; 32 char rcv_buf[MAXDATASIZE]; /*./client 127.0.0.1 hello */ 33 char snd_buf[MAXDATASIZE]; 34 struct hostent *host; /* struct hostent 35 * { 36 * char *h_name; // general hostname 37 * char **h_aliases; // hostname's alias 38 * int h_addrtype; // AF_INET 39 * int h_length; 40 * char **h_addr_list; 41 * }; 42 */ 43 struct sockaddr_in server_addr; 44 45 46 /* */ 47 fd_set readset, writeset; 48 int check_timeval = 1; 49 struct timeval timeout={check_timeval,0}; //阻塞式select, 等待1秒,1秒轮询 50 int maxfd; 51 int fp; 52 int cir_count = 0; 53 int ret; 54 55 56 if (argc < 3) 57 { 58 printf("Usage:%s [ip address] [any string]\n", argv[0]); 59 return 1; 60 } 61 62 63 *snd_buf = '\0'; 64 strcat(snd_buf, argv[2]); 65 66 67 if ((fp = open(TFILE,O_WRONLY)) < 0) //不是用fopen 68 { 69 perror("fopen:"); 70 exit(1); 71 } 72 73 74 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 75 { 76 perror("socket:"); 77 exit(1); 78 } 79 80 81 server_addr.sin_family = AF_INET; 82 server_addr.sin_port = htons(SERVPORT); 83 inet_pton(AF_INET, argv[1], &server_addr.sin_addr); 84 memset(&(server_addr.sin_zero), 0, 8); 85 86 87 /* create the connection by socket 88 * means that connect "sockfd" to "server_addr" 89 */ 90 if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) 91 { 92 perror("connect"); 93 exit(1); 94 } 95 96 97 /**/ 98 if (send(sockfd, snd_buf, sizeof(snd_buf), 0) == -1) 99 {100 perror("send:");101 exit(1);102 }103 printf("send:%s\n", snd_buf);104 105 while (1)106 {107 FD_ZERO(&readset); //每次循环都要清空集合,否则不能检测描述符变化108 FD_SET(sockfd, &readset); //添加描述符 109 FD_ZERO(&writeset);110 FD_SET(fp, &writeset);111 112 maxfd = sockfd > fp ? (sockfd+1) : (fp+1); //描述符最大值加1113 114 ret = select(maxfd, &readset, NULL, NULL, NULL); // 阻塞模式115 switch( ret)116 {117 case -1:118 exit(-1);119 break;120 case 0:121 break;122 default:123 if (FD_ISSET(sockfd, &readset)) //测试sock是否可读,即是否网络上有数据124 {125 recvbytes = recv(sockfd, rcv_buf, MAXDATASIZE, MSG_DONTWAIT);126 rcv_buf[recvbytes] = '\0';127 printf("recv:%s\n", rcv_buf);128 129 if (FD_ISSET(fp, &writeset))130 {131 write(fp, rcv_buf, strlen(rcv_buf)); // 不是用fwrite132 }133 goto end;134 }135 }136 cir_count++;137 printf("CNT : %d \n",cir_count);138 }139 140 end:141 close(fp);142 close(sockfd);143 144 145 return 0;146 }147 148 perl实现:149 #! /usr/bin/perl150 ###############################################################################151 # \File152 # tcp_client.pl153 # \Descript154 # send message to server155 ###############################################################################156 use IO::Socket;157 use IO::Select;158 159 160 #hash to install IP Port161 %srv_info =(162 #"srv_ip" => "61.184.93.197",163 "srv_ip" => "192.168.1.73",164 "srv_port"=> "8080",165 );166 167 168 my $srv_addr = $srv_info{ "srv_ip"};169 my $srv_port = $srv_info{ "srv_port"};170 171 172 my $sock = IO::Socket::INET->new(173 PeerAddr => "$srv_addr",174 PeerPort => "$srv_port",175 Type => SOCK_STREAM,176 Blocking => 1,177 # Timeout => 5,178 Proto => "tcp")179 or die "Can not create socket connect. $@";180 181 182 $sock->send("Hello server!\n", 0) or warn "send failed: $!, $@";183 $sock->autoflush(1);184 185 186 my $sel = IO::Select->new($sock);187 while(my @ready = $sel->can_read)188 {189 foreach my $fh(@ready)190 {191 if($fh == $sock)192 {193 while()194 {195 print $_;196 }197 $sel->remove($fh);198 close $fh;199 }200 }201 }202 $sock->close();203 204 四、异步非阻塞模式205 最后,异步非阻塞 I/O 模型是一种处理与 I/O 重叠(并行)进行的模型。206 以read系统调用为例207 steps:208 a. 调用read;209 b. read请求会立即返回,说明请求已经成功发起了。210 c. 在后台完成读操作这段时间内,应用程序可以执行其他处理操作。211 d. 当 read 的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次 I/O 处理过程。212 213 /*214 * \brief215 * tcp client216 */217 218 #include
219 #include
220 #include
221 #include
222 #include
223 #include
224 #include
225 226 #include
227 #include
228 #include
229 #define SERVPORT 8080230 #define MAXDATASIZE 100231 #define TFILE "data_from_socket.txt"232 233 234 int main(int argc, char *argv[])235 {236 int sockfd, recvbytes;237 char rcv_buf[MAXDATASIZE]; /*./client 127.0.0.1 hello */238 char snd_buf[MAXDATASIZE];239 struct hostent *host; /* struct hostent240 * {241 * char *h_name; // general hostname242 * char **h_aliases; // hostname's alias243 * int h_addrtype; // AF_INET244 * int h_length; 245 * char **h_addr_list;246 * };247 */248 struct sockaddr_in server_addr;249 250 251 /* */252 fd_set readset, writeset;253 int check_timeval = 1;254 struct timeval timeout={check_timeval,0}; //阻塞式select, 等待1秒,1秒轮询255 int maxfd;256 int fp;257 int cir_count = 0;258 int ret;259 260 261 if (argc < 3)262 {263 printf("Usage:%s [ip address] [any string]\n", argv[0]);264 return 1;265 }266 267 268 *snd_buf = '\0';269 strcat(snd_buf, argv[2]);270 271 272 if ((fp = open(TFILE,O_WRONLY)) < 0) //不是用fopen273 {274 perror("fopen:");275 exit(1);276 }277 278 279 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)280 {281 perror("socket:");282 exit(1);283 }284 285 286 server_addr.sin_family = AF_INET;287 server_addr.sin_port = htons(SERVPORT);288 inet_pton(AF_INET, argv[1], &server_addr.sin_addr);289 memset(&(server_addr.sin_zero), 0, 8);290 291 292 /* create the connection by socket 293 * means that connect "sockfd" to "server_addr"294 */295 if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)296 {297 perror("connect");298 exit(1);299 }300 301 302 /**/303 if (send(sockfd, snd_buf, sizeof(snd_buf), 0) == -1)304 {305 perror("send:");306 exit(1);307 }308 printf("send:%s\n", snd_buf);309 310 311 while (1)312 {313 FD_ZERO(&readset); //每次循环都要清空集合,否则不能检测描述符变化314 FD_SET(sockfd, &readset); //添加描述符 315 FD_ZERO(&writeset);316 FD_SET(fp, &writeset);317 318 maxfd = sockfd > fp ? (sockfd+1) : (fp+1); //描述符最大值加1319 320 ret = select(maxfd, &readset, NULL, NULL, &timeout); // 非阻塞模式321 switch( ret)322 {323 case -1:324 exit(-1);325 break;326 case 0:327 break;328 default:329 if (FD_ISSET(sockfd, &readset)) //测试sock是否可读,即是否网络上有数据330 {331 recvbytes = recv(sockfd, rcv_buf, MAXDATASIZE, MSG_DONTWAIT);332 rcv_buf[recvbytes] = '\0';333 printf("recv:%s\n", rcv_buf);334 335 336 if (FD_ISSET(fp, &writeset))337 {338 write(fp, rcv_buf, strlen(rcv_buf)); // 不是用fwrite339 }340 goto end;341 }342 }343 timeout.tv_sec = check_timeval; // 必须重新设置,因为超时时间到后会将其置零344 345 cir_count++;346 printf("CNT : %d \n",cir_count);347 }348 349 end:350 close(fp);351 close(sockfd);352 353 return 0;354 }355 356 五、server端程序:357 /*358 * \brief359 * tcp server360 */361 #include
362 #include
363 #include
364 #include
365 #include
366 #include
367 #include
368 #define SERVPORT 8080369 #define BACKLOG 10 // max numbef of client connection370 #define MAXDATASIZE 100371 372 373 int main(char argc, char *argv[])374 {375 int sockfd, client_fd, addr_size, recvbytes;376 char rcv_buf[MAXDATASIZE], snd_buf[MAXDATASIZE];377 char* val;378 struct sockaddr_in server_addr;379 struct sockaddr_in client_addr;380 int bReuseaddr = 1;381 382 383 char IPdotdec[20];384 385 386 /* create a new socket and regiter it to os .387 * SOCK_STREAM means that supply tcp service, 388 * and must connect() before data transfort.389 */390 if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)391 {392 perror("socket:");393 exit(1);394 }395 396 /* setting server's socket */397 server_addr.sin_family = AF_INET; // IPv4 network protocol398 server_addr.sin_port = htons(SERVPORT);399 server_addr.sin_addr.s_addr = INADDR_ANY; // auto IP detect400 memset(&(server_addr.sin_zero),0, 8);401 402 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(int));403 if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr))== -1)404 {405 perror("bind:");406 exit(1);407 }408 409 /* 410 * watting for connection , 411 * and server permit to recive the requestion from sockfd 412 */413 if (listen(sockfd, BACKLOG) == -1) // BACKLOG assign thd max number of connection414 {415 perror("listen:");416 exit(1); 417 } 418 419 while(1) 420 { 421 addr_size = sizeof(struct sockaddr_in); 422 423 /* 424 * accept the sockfd's connection, 425 * return an new socket and assign far host to client_addr 426 */ 427 printf("watting for connect...\n"); 428 if ((client_fd = accept(sockfd, (struct sockaddr *)&client_addr, &addr_size)) == -1) 429 { 430 /* Nonblocking mode */ 431 perror("accept:"); 432 continue; 433 } 434 435 /* network-digital to ip address */ 436 inet_ntop(AF_INET, (void*)&client_addr, IPdotdec, 16); 437 printf("connetion from:%d : %s\n",client_addr.sin_addr, IPdotdec); 438 439 //if (!fork()) 440 { 441 /* child process handle with the client connection */ 442 443 /* recive the client's data by client_fd */ 444 if ((recvbytes = recv(client_fd, rcv_buf, MAXDATASIZE, 0)) == -1) 445 { 446 perror("recv:"); 447 exit(1); 448 } 449 rcv_buf[recvbytes]='\0'; 450 printf("recv:%s\n", rcv_buf); 451 452 453 *snd_buf='\0'; 454 strcat(snd_buf, "welcome"); 455 456 sleep(3); 457 /* send the message to far-hosts by client_fd */ 458 if (send(client_fd, snd_buf, strlen(snd_buf), 0) == -1) 459 { 460 perror("send:"); 461 exit(1); 462 } 463 printf("send:%s\n", snd_buf); 464 465 close(client_fd); 466 //exit(1); 467 } 468 469 //close(client_fd); 470 }471 472 return 0; 473 }
View Code

 

转载于:https://www.cnblogs.com/lihonglin2016/p/4433176.html

你可能感兴趣的文章
Objective-C 使用 C++类
查看>>
浅谈之高级查询over(partition by)
查看>>
Notes: CRM Analytics–BI from a CRM perspective (2)
查看>>
graphite custom functions
查看>>
列出所有的属性键
查看>>
js获取请求地址后面带的参数
查看>>
[原创]使用java批量修改文件编码(ANSI-->UTF-8)
查看>>
设计模式のCompositePattern(组合模式)----结构模式
查看>>
二进制集合枚举子集
查看>>
磁盘管理
查看>>
SAS学习经验总结分享:篇二—input语句
查看>>
UIImage与UIColor互转
查看>>
RotateAnimation详解
查看>>
系统管理玩玩Windows Azure
查看>>
c#匿名方法
查看>>
如何判断链表是否有环
查看>>
【小程序】缓存
查看>>
ssh无密码登陆屌丝指南
查看>>
MySQL锁之三:MySQL的共享锁与排它锁编码演示
查看>>
docker常用命令详解
查看>>