1 / 37

架构平台部 < 网络通讯 >培训

架构平台部 < 网络通讯 >培训. 讲师: nekeyzhong 日期:2009年 8 月 21 日. 课程简介. 网络通讯,socket实现tcp,udp。以及select,epoll等功能函数应用. 背景知识回顾. OSI 七层模型: tcp/ip 五层模型:. tcp/ip 四层模型:. Unix基本Socket API介绍1. Unix基本Socket API介绍2. // 客户端 struct sockaddr_in servaddr; int sockfd = socket(AF_INET, SOCK_STREAM, 0);

amos
Download Presentation

架构平台部 < 网络通讯 >培训

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 架构平台部<网络通讯>培训 讲师:nekeyzhong 日期:2009年8月21日

  2. 课程简介 • 网络通讯,socket实现tcp,udp。以及select,epoll等功能函数应用

  3. 背景知识回顾 OSI 七层模型: tcp/ip 五层模型: tcp/ip 四层模型:

  4. Unix基本Socket API介绍1

  5. Unix基本Socket API介绍2

  6. // 客户端 struct sockaddr_in servaddr; int sockfd = socket(AF_INET, SOCK_STREAM, 0); servaddr.sin_family = AF_INET; servaddr.sin_port =htons(18000); servaddr.sin_addr.s_addr = inet_addr("192.168.1.100"); connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); write(sockfd,"hello",strlen("hello")); memset(szRecv,0,sizeof(szRecv)); int n = read(sockfd,szRecv,sizeof(szRecv)); printf("%s\n",szRecv); close(sockfd); 简单的TCP客户端代码

  7. 简单的TCP服务器代码 int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server; server.sin_addr.s_addr = INADDR_ANY; servaddr.sin_family = AF_INET; servaddr.sin_port = htons(18000); bind(listenfd, &servaddr, sizeof(servaddr)); listen(listenfd, 10); while(1){ clilen = sizeof(cliaddr); int newclientfd = accept(listenfd, &cliaddr, &clilen); n = read(newclientfd ,sRecv,sizeof(sRecv)); if(n == 0) close(newclientfd ); n = write(newclientfd , sRecv, n); close(newclientfd ); }

  8. 简单的UDP客户端代码 char buf[1024]; servaddr.sin_family = AF_INET; struct sockaddr_in server; server.sin_port = htons(8888); server.sin_addr.s_addr = inet_addr("192.168.1.100"); int sockfd = socket(AF_INET,SOCK_DGRAM,0); while(1){ strcpy(buf,"hello"); int ret = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr *)&server,sizeof(struct sockaddr_in)); socklen_t len = sizeof(struct sockaddr_in); ret = recvfrom(sockfd,buf,1024,0,(struct sockaddr *)&server,(socklen_t*)&len); printf("redv:%s\n",buf); }

  9. 简单的UDP服务器代码 struct sockaddr_in server ; servaddr.sin_family = AF_INET; server.sin_port = htons(8888); server.sin_addr.s_addr = INADDR_ANY; struct sockaddr_in client; sockfd = socket(AF_INET,SOCK_DGRAM,0); bind(sockfd,(struct sockaddr *)&server,sizeof(struct sockaddr_in)); int iclientlen = sizeof(struct sockaddr_in); while(1){ int recvlen= recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&client, (socklen_t*)&iclientlen); sendto(sockfd,buf,recvlen,0,(struct sockaddr *)&client,sizeof(struct sockaddr_in)); }

  10. UDP的显式connect • udp也可以使用connect来指明对方ip,port • 使用connect后,udp可以使用write,read等函数发送数据,无需指明对方地址。 • 只要知道对方的ip,port就可以发送数据给对方。 • recvfrom 可能收到任何对端的数据,不一定是你期待的对端。例如客户端调用recvfrom得到的数据可能不是sendto所指明的服务器发送的。 • 使用connect可以指明对端,read时只能收到此对端的数据。 • 由于udp可以使用一个socket像任何对端发送数据,也可以使用一个socket从任何对端接收数据,所以它只需要一个socket。

  11. TCP与UDP的差异 不可靠的传送 64K,大了要分包 一个socket可以处理所有client,server 面向报文的数据发送,IP报文不会合并或拆分 无连接概念,只要ip,port即可通讯。 无流控,快的淹没慢的。 • 可靠的传送 • 报文无大小限制 • 一个socket对应一对client,server • 流式数据发送,IP报文可合并或拆分。 • 需要建立连接,并维护。 • 有流控,不会淹没 流式数据在接收时面临怎样的问题?

  12. TCP的连接过程(3次握手) • 3次握手 主动方 消息 被动方 connect() --SYN--> SYN_SEND SYN_RECV <-- SYN+ACK-- ESTABLISHED -- ACK --> ESTABLISHED • 失败流程: SYN ----------------> 关闭的端口 <---------RST----

  13. TCP状态机 状态机 连接管理 定时器 拥塞控制算法 安全

  14. TCP的断接过程 close() ------ FIN -------> FIN_WAIT1 CLOSE_WAIT <----- ACK ------- FIN_WAIT2 close() 调用 <------ FIN ------ LAST_ACK TIME_WAIT ------ ACK -------> (wait...) CLOSED CLOSED

  15. 一个工具 tcpdump • tcpdump -X -s 0 port 39111 and host 172.27.196.237

  16. 字节序 什么是字节序?字节序由什么决定? 网络字节序是big-endian intel x86是little-endian sender htonl(int) recver ntohl(int)

  17. 阻塞与非阻塞的问题 什么是阻塞,非阻塞? connect,accept,read,write 默认情况下,行为均是阻塞的。 read 在未读到数据时不会返回。 write 在数据未完全发完时不会返回。 过多的等待,一个流程大部分时间都在等待,无事可做... 一次只能在一个连接上等待。 怎么解决? 1.单流程并发 2.多流程 3.非阻塞流程

  18. 单流程并发(Select 多路监听) listenfd clientfd[128] fd_set allSet; FD_ZERO(&allSet); //每次select之前必须重新设置fd_set FD_SET(listenfd,&allSet); FD_SET(clientfd[0-127],&allSet); //设入所有有效的clientfd if (select(FD_SETSIZE, &read_set,NULL, NULL, NULL) > 0){ for (int i = 0; i < 127; ++i){ if (FD_ISSET(clientfd[i], &read_set)){ ProcessRecv(clientfd[i]); } if (FD_ISSET(listenfd, &read_set)) { Accept_New(listenfd);//将得到的clientfd放入数组 } } select返回0说明什么?read返回0说明什么? 本质上还是阻塞,write仍然存在很多不必要的等待。

  19. 多流程并发 • 多流程并发 1.多进程并发 a.主进程accept后,fork一个子进程处理. b.所有进程竞争accept c.主进程accept后,选择一个子进程,将fd交给此进程处理。 2.多线程并发 a.一个线程调用accept产生fd到队列中,其余线程竞争处理此队列中的fd

  20. 多流程并发之----多进程并发1 • 多进程并发1 主进程accept后,fork一个子进程处理。子进程只处理一个连接. while(1){ struct sockaddr_in cli_addr; socklen_t cli_len = sizeof (cli_addr); newsockfd = accept (sockfd, (struct sockaddr *) &cli_addr, &cli_len); pid_t pid = fork(); if (pid ==0){ do_work(newsockfd); close(newsockfd); exit(0); } }

  21. 多流程并发之----多进程并发2 • 多进程并发2 所有进程竞争accept(惊群现象) bind(listenfd, ...); listen(listenfd); // 创建进程 for (int i = 0; i < forknum; ++i){ pid = fork(); if (pid == 0) break; } // 多个进程竞争accept for (;;){ connfd = accept(listenfd, ...); if(connfd >=0) child_process(listendfd); }

  22. 多流程并发之----多进程并发3 • 多进程并发2 选择一个子进程,将fd交给此进程处理 for (;;){ connfd = accept(listenfd, ...); for (i = 0; i < childnum; ++i){ if (child[i].status == 0) // 这个子进程不忙,通过管道传送fd,将任务发给它 write(child[i].pipefd, &connfd,sizeof(connfd)); break; } } 没有竞争,没有惊群,但需要维护复杂的进程状态。

  23. 多流程并发之----多线程并发 • 多线程并发 线程A accept描述符fd到一个队列,线程N1,N2。。。从队列竞争得到fd. 临界资源,需要保护

  24. 多流程的缺点 • 各流程之间竞争cpu资源,切换代价大。 • 多少个流程是合适的? • 单个流程阻塞严重时,需要更多的流程,更多的系统资源。 • 性能有上限,总能力=单个流程能力*流程数 怎么解决? 非阻塞

  25. 非阻塞socket • read 将OS缓存中的数据读出,无数据时不会等待。 • write 将数据写到OS缓存后马上返回,能写多少写多少。 应用程序调用 read write OS系统缓存 read_buf write_buf 网络传输 物理网络 设置非阻塞: int fdctl = fcntl(socket,F_GETFL); fcntl(socketfd,F_SETFL,fdctl | O_NONBLOCK); 设置/获取系统socket缓存大小: setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const void *)&iOptVal, iOptLen); getsockopt(...)

  26. 非阻塞socket • 由于没有阻塞点,只需要一个流程即可。 • write不保证数据全部能否发送完毕,所以需要程序需要自己保存未发完数据,等待下次发送。 • 非阻塞特性可以通过accpet继承下来。 int iWLen = write(clientfd,szBuff,iDataLen); if(iWLen < iDataLen){ append_to_buffer(szBuff+iWLen,iDataLen-iWLen ); } 复杂性在于需要对每一个链接维护状态和残余数据。

  27. 非阻塞socket返回值 send > 0 等于申请发送的长度,一切OK 小于申请发送的长度,缓冲满,部分数据未发送。 send < 0 errno等于EAGAIN,此次调用失败,socket正常 errno等于其它值,socket异常,需要关闭。 recv < 0 errno等于EAGAIN,此次调用失败,socket正常 errno等于其它值,socket异常,需要关闭。 recv=0 对端关闭。

  28. 比select更快的epoll • Epoll 一种新的I/O多路复用技术。 • 传统的select以及poll的效率会因为在线人数的线形递增而导致呈二次乃至三次方的下降,这些直接导致了网络服务器可以支持的人数有了个比较明显的限制。 • select 管理的描述符最多1024个。 • 基于性能问题,使用epoll替代select。

  29. 性能对比

  30. Epoll 函数 • 函数声明:int epoll_create(int size)该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围 • 函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。 • 参数:epfd:由epoll_create生成的epoll专用的文件描述符;op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD注册、EPOLL_CTL_MOD修改、EPOLL_CTL_DEL删除fd:关联的文件描述符;event:指向epoll_event的指针; 例如: ev.data.fd = newSocket; ev.events = EPOLLIN | EPOLLERR | EPOLLOUT; • 如果调用成功返回0,不成功返回-1

  31. Epoll 函数 • 函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)该函数用于轮询I/O事件的发生; 参数:epfd:由epoll_create 生成的epoll专用的文件描述符;epoll_event:用于回传代处理事件的数组;maxevents:每次能处理的事件数;timeout:等待I/O事件发生的超时值; 返回发生事件数。

  32. events字段 • EPOLLIN:表示对应的文件描述符可以读 • EPOLLOUT:表示对应的文件描述符可以写 • EPOLLPRI:表示对应的文件描述符有紧急的数据可读 • EPOLLERR:表示对应的文件描述符发生错误 • EPOLLHUP:表示对应的文件描述符被挂断 • EPOLLET/EPOLLLT:表示事件触发模式 Edge Triggered (ET) Level Triggered (LT)

  33. 对比 FD_SET(socketfd, &rfds); FD_SET(socketfd, &wfds); epoll_ctl(epollfd, EPOLL_CTL_ADD, newSocket, &ev) select(maxfd+1, &rfds, NULL, NULL, &timeout) epoll_wait(epollfd, events, MaxfdNum,timeout) FD_ISSET(socket, rfds) FD_ISSET(socket, wfds) if(events[i]->events &EPOLLOUT)

  34. example if(events[i].data.fd==listenfd) accept(listenfd...); else if(events[i].events & EPOLLIN) read(events[i].data.fd... else if(events[i].events & EPOLLOUT) write(events[i].data.fd... else { close(events[i].data.fd); epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd, &ev); } struct epoll_event ev,events[MAXSOCKETNUM]; epfd=epoll_create(MAXSOCKETNUM); listenfd = socket(AF_INET, SOCK_STREAM, 0); bind(listenfd,(sockaddr *) serveraddr, sizeof(serveraddr)); listen(listenfd, 1024); ev.data.fd=listenfd; ev.events=EPOLLIN; epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd, ev); while(1){ nfds=epoll_wait(epfd,events,MAXSOCKETNUM,timeout); for(i=0;i<nfds;++i) {

  35. 推荐书目: • <Unix網絡編程 > 淸華大學詘版社

  36. 课后作业 • 作业内容: 做一个http服务器,提供下载文件功能。 要求:在浏览器地址栏输入:http://172.16.16.16:8080/htdocs/aaa.bmp,能够下载一个文件 http://172.16.16.16:8080/htdocs/aaa.html,能够提供网页浏览 http协议自己找资料 • 作业要求: • 性能要尽可能的高,选用合理的模型 • 作业例子程序,附件

  37. 谢谢!

More Related