TCP/IP(7)——网络编程(2)多线程与并发式网络编程+RAW套接口+广播报文收发
我之前写过TCP/IP编程——多线程+非阻塞的服务实现,这篇文章是实现之前需要掌握的知识。
目录
我们网络编程(1)里介绍了单线程的TCP协议。
但是一般生活中,我们的服务都是多线程,并发的,并且是非阻塞的,有利于效率。
在win32库中提供了创建多线程的函数_beginthread与I/O复用函数(实现非阻塞)select
多线程
1.定义多线程的线程函数void workthread(LPVOID lpParam) 【返回值必须是void,传递参数可以用32bit的输入参数,即LPVPOD,LPVOID是一个没有类型的指针】
2.开启线程HANDLE hThread = (HANDLE) _beginthread(workthread, 0, hIOCP)【如果线程创建成功,函数会返回新线程的句柄】
3._endthread函数可以关闭线程。当线程执行完毕也会自动调 用该函数,该函数将释放线程中的所有资源。
多线程同步:锁
win32应用的事件对象机制可以用来通知等待线程某个条件已经满足。
如WriteThread在写完了缓存区的数据后,调用WSASetEvent(evToRead)便可以通知其他在等待的线程执行
没执行WSASetEvent(evToRead)之前,ReadThread通过WSAWaitForMultipleEvents (*,&evToRead,*,*,*)挂起等待事件对象被通知。
当然这两个函数只是win32提供的基础线程同步,还有DWORD WSAAPI WSAWaitForMultipleEvents等函数
非阻塞
调用select可以实现非阻塞。传统recv调用后会阻塞执行直到有数据可读。而select可以同时判断一个套接口(或者套接口集合,fd_set)的多种I/O状态(read、write、exception异常)。每次select返回都至少有一个套接口满足了I/O要求。
函数定义: int select( int nfds, fd_set FAR *readfds, fd_set FAR *writefds fd_set FAR *exceptfds, const struct timeval FAR *timeout);
参数含义: nfds 【 IN 】 : 同时select的套接口个数,已被忽略。
readfds 、 writefds 、 exceptfds 【 INOUT 】 : 描述字集合fd_set指针。
timeout【IN】:以TIMEVAL的形式告诉select函数需要等待的时间
图形化代码流程:
select的基本操作:FD_ZERO-->FD_SET-->SELECT-->FD_ISSET -->I/O操作
- FD_ZERO后fdset的参数fdcount=0:没有fd在fdset中
- FD_SET后fdset中的fdcount是加入的fd的数量,然后调用select
- 事件发生后select返回的是一个fdset,这个set中的个数即满足条件的socket的个数
- 调用FD_ISSET查看fd是不是在返回的set中
2之后: 3返回的结果
用例1:接受请求的非阻塞实现
[code]//阻塞实现 listen(socklisten, 5); while(1) { sAccept = accept(socklisten, struct sockaddr *)&cli, &ilen); } //非阻塞实现 listen(socklisten, 5); SOCKET socka;//socka是用来接受请求的socket,和原来完成三次握手的不一样。 fd_set rfd; //readfds结构体 struct timeval timeout;//设置等待时间的结构体 timeout.tv_sec=60; //最多等待60s timeout.tv_usec=0; while (1) { FD_ZERO(&rfd);//清空rfd FD_SET(socklisten, &rfd);//将监听原来的socketlisten加入到rfd中 ret = select(0, &rfd, 0, 0, &timeout);//检查套节字是否可读, if(FD_ISSET(sock, &rfd)){//如果套接字句柄还在fd_set里,说明客户端已经有connect的请求发过来了,马上可以accept成功 socka=accept(socklisten,0,0); // } }
更多demo请见:TCP/IP编程——多线程+非阻塞的服务实现
UDP
上面介绍的都是TCP基于流式数据传输,下面我们介绍基于数据报数据传输的UDP
数据报特性:在网络中独立传输,每个报文自身都包含地址、端口 号信息;
与TCP程序最大的不同是:UDP程序在客户和服务器之间不需要先建立连接,通信的任何一方可以先发送数据,而另一方直接接收就可以了。
这是普通的UDP迭代式程序模型,没有使用connect绑定。
TCP与UDP的区别
connect函数在UDP中不再发起三次握手而是建立起一个对应关系
区别1:socket函数
于UDP协议,socket函数使用如下
- SOCKET s = socket(AF_INET, SOCK_DGRAW, IPPROTO_UDP)
- SOCKET s = socket(AF_INET, SOCK_DGRAW, 0)
- SOCKET s = WSASocket(AF_INET, SOCK_DGRAW, IPPROTO_UDP, NULL, WSA_FLAG_OVERLAPPERD)
区别2:connect函数
Connect函数不再发起三次握手,而是底层建立一个连接对应关系,发送时无需用sendto设置目标地址,直接用send即可,接收时只接收对应源地址的报文,即通过 recv即可接收报文。
区别3:sendto/recvfrom函数
如果不connect需要用sendto/recvfrom函数替换send/recv
五元组标识TCP连接
五元组标识UDP连接
Raw Socket接口
在程序中使用原始套接口(Raw Socket),就可以绕过需要TCP/IP 的传输层协议,直接对TCP/IP的底层进行操作,如对ICMP和IP进行实际 操作。
使用原始套接口的典型代码如下
[code]SOCKET s; s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
此处不细说,感兴趣请自行百度RawSocket的ping实现。
广播报文收发
只有UDP套接口才能实现广播和多播,但是默认不广播,需要配置。
使用setsockopt和getsockopt函数可以设置或读取套接口的选项值
[code]//判断socket是否支持广播 Bool bBroadcast; int optlen = sizeof(bBroadcast); if(getsockopt(sock, SOLSOCKET, SO_BROADCAST, (char *)&bBroadcast, &optlen) == SOCKET_ERROR){ //使用getsockopt读取套接口的选项值 HandleError(“getsockopt”); closesocket(sock); WSACleanup(); return -1; } if (bBroadcast) printf(“Broadcast enabled default!\n”); else printf(“Broadcast disabled default!\n”); ———————————————————————————————————————————————————————————— //设置socket支持广播 Bool bBroadcast = true; optlen = sizeof(bBroadcast); if(setsockopt(sock, SOLSOCKET, SO_BROADCAST, (char *)&bBroadcast, &optlen) == SOCKET_ERROR) { HandleError(“setsockopt”); closesocket(sock); WSACleanup(); return -1; }
多播报文设计
广播一开始是为了用于资源发现和减少数据交互量。但是无论有没有参与广播应用,同一网段都需要对广播数据报进行处理,所以广播都会传到系统的协议栈中的监听端口的应用或者丢弃。
那为了解决这种无用功耗,多播产生了,只有加入多播地址的用户才会处理多播报文。
本地接口加入多播组
[code]Struct ip_mreq mreq; Memcpy(&(mreq.imr_interface), local_if, sizeof(struct in_addr)); Memcpy(&(mreq.imr_multiaddr), mcaddr, sizeof(struct in_addr)); Setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&mreq, sizeof(mreq));//IP_ADD_MEMBERSHIP告诉本机的IP层和数据链路层接收发送到这个组的多播数据
退出多播组
[code]Struct ip_mreq mreq; Memcpy(&(mreq.imr_interface), local_if, sizeof(struct in_addr)); Memcpy(&(mreq.imr_multiaddr), mcaddr, sizeof(struct in_addr)); Setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, sizeof(mreq));//IP_DROP_MEMBERSHIP
- C#开发之Socket网络编程TCP/IP层次模型、端口及报文等探讨
- C#开发之Socket网络编程TCP/IP层次模型、端口及报文等探讨
- C#中开发之Socket网络编程TCP/IP层次模型、端口及报文等探讨
- TCP、UDP、IP报文格式
- TCP-IP详解卷1-06:ICMP:Internet控制报文协议(Internet Control Message Protocol)
- Windows Socket 网络编程(一) —— TCP/IP体系结构、特点及相关术语
- IP TCP以及HTTP报文gesh
- TCP/IP第四层--传输层TCP数据报文详解
- TCP/IP第四层--传输层TCP数据报文详解
- Tcp/ip 报文解析
- 2015-07学习总结——网络编程(TCP/IP)
- select连接TCPIP数据收发测试(连接多客户)
- Windows Socket 网络编程(一) —— TCP/IP体系结构、特点及相关术语
- TCP/IP 协议簇下的各报文结构总结
- 网络编程培训之一 编程实现IP/TCP/UDP报文
- 9.TCP/IP和UDP的socket网络编程
- 多线程TCP/IP通讯的服务端
- TCP、UDP、IP、ARP的报文格式以及所代表的含义
- TCP/IP第四层--传输层TCP数据报文详解
- 网络编程通信协议————TCP/IP