(1)定义差别[1]
数据报是网络传输的数据的基本单元,包含一个报头和数据本身,其中报头描述了数据的目的地以及和其它数据之间的关系。同一报文的不同分组可以由不同的传输路径通过通信子网。UDP基于数据报。
字节流方式指的是仅把传输中的报文看作是一个字节序列,在字节流服务中,由于没有报文边界,用户进程在某一时刻可以读或写任意数量的字节。 TCP基于字节流。
(2)接收方式
TCP方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
needbytes = RECVBUFSIZE-1; while( bytesRecv <needbytes ) //取够进行处理,可以挂接到队列里 { needbytes -= bytesRecv ; bytesRecv = recv( m_socket, buf, needbytes , 0 ); if ( bytesRecv == 0 || bytesRecv == WSAECONNRESET ) {//socket关闭了 break; } buf+=bytesRecv; //偏移 } |
UDP方式:
1 2 3 4 5 6 7 8 |
while(bRun) { iRes = recvfrom(m_socket,buf,RECVBUFSIZE-1,0,(struct sockaddr FAR *)&from,(int FAR *)&fromlength); if(iRes >=0) { break; //接收到了一个数据包,进行必要的处理,如果buffer大小小于数据报的长度,剩余丢弃 } } |
注:If the datagram or message is larger than the buffer specified, the buffer is filled with the
first part of the datagram, and recv generates the error WSAEMSGSIZE. For unreliable protocols
(for example, UDP) the excess data is lost; for reliable protocols, the data is retained by the
service provider until it is successfully read by calling recv with a large enough buffer.
(3)处理大数据包或本地读出缓存小存在的问题
Posix系列的recv、recvfrom、read函数均无法得到数据包被截断的错误消息,只有recvmsg可以得到该消息。 ssize_t recvmsg(int socket, struct msghdr *message, int flags); 如果message->msg_flags & MSG_TRUNC为真,则表示数据包被截断。超出部分被丢弃。但也有例外,Solaris并不设置MSG_TRUNC,直接丢弃超出部分。 SVR4系统不丢弃超出部分,在后续的读操作中会获取超出部分。 Windows下的recv、recvfrom、WSARecv、WSARecvFrom会返回-1,并设置Last Error为WSAEMSGSIZE。另外,WSARecvEx函数则是专门用于这方面的一个函数。 int PASCAL FAR WSARecvEx(SOCKET s, char* buf, int len, int* flags); 当*flags & MSG_PARTIAL为真实,表示数据包被截断 Windows下所有超出的数据包都会被丢弃。
(4)疑问
recvfrom 与 recv均可以用在基于链接的或非链接的socket,两者就是接口不同吗?
这两个函数的区别仅仅在于,recvfrom会记录对方的socket地址,recvfrom有一个容易出错的地方,就是关于from地址的长度,在调用时这个长度必须是结构体struct sockaddr的长度,因为在recvfrom的实现中将会用到这个值。Recv/send中的flag如果是0,那么这两个函数等同于read/write. 需要注意的是,系统的socket缓冲区中并没有对收到的数据作字符串式的结尾处理,所以分析报文需要时需要用到recv/recvfrom返回的实际读取长度。