Windows网络编程学习笔记(6) Socket关闭、流式协议、分组-重组I/O
来源:互联网 发布:网络武侠小说排行榜 编辑:程序博客网 时间:2024/06/09 21:55
Socket的关闭与流式协议(Stream Protocols)、分组-重组I/O(Scatter-Gather I/O)
Socket关闭连接shutdown()/closesocket()函数
一旦你使用完一个Socket连接后,需要及时关闭它来释放相关资源。释放一个Socket句柄关联的资源需要调用 closesocket() 函数。
然而closesocket() 函数在某些特定环境下会造成数据丢失的负面影响。
因此在调用closesocket()之前,需要先使用 shutdown() 函数关闭连接以确保数据被对方收到。
接下来介绍这两个函数。
shutdown()函数
一个好的程序退出前必须通知对方数据已经发送完毕,对方也要做样的事。这被称之为完整关闭 graceful close,依赖shutdown()来实现。
int shutdown(
SOCKET s,
int how
);
int how 参数可以是:SD_RECEIVE, SD_SEND, 或 SD_BOTH.
SD_RECEIVE: 指定socket 接下来不能再调用接收数据的函数,这对更底层的协议没有影响。
另外,对于TCP sockets,如果数据以队列形式接收或者数据后来到达了,连接都会拒绝。
然而,对于UDP sockets,接下来的数据仍然会被接收并被放入接收队列,(因为shutdown对于无连接协议没有意义)
SD_SEND: 指定socket 接下来不能再调用发送数据的函数。
对于TCP sockets,所有数据发送完毕和被确认接收后,将发送一个FIN。
SD_BOTH: 既有SD_RECEIVE又有SD_SEND的效果。
注意: 不是所有面向连接协议支持完整关闭(graceful close)、提供shutdown()的功能。
有些协议(如 ATM)只需要调用closesocket()来终结会话。
closesocket()函数
int closesocket (SOCKET s);调用closesocket(SOCKET s)时将释放该socket句柄和调用该socket发生WSAENOTSOCK失败时产生的更深层次的调用。
与该socket句柄相关的资源都会被释放,包括缓冲队列中的数据将会被丢弃。
在此过程中任何线程发起的挂起同步调用(pending syschronous calls)将会被取消并且不发送任何通知消息。
挂起重叠操作(pending overlapped)也会被取消。任何事件、例程将会以WSA_OPERATION_ABORTED错误失败。
有关重叠操作等会在后面详解,此处可以略过。
流式协议(Stream Protocols)
大多数面向连接通讯都是流式协议。流式协议:发送方和接收方将数据进行拆分成多个分组并重组。
注意:流式协议中你不能确保你使用 发送/接收(recv()/send()) 函数传输的数据量大小正好是你所请求的。
char sendbuff[2048]; //发送缓冲区
int nBytes = 2048; //发送数据大小
ret = send(s, sendbuff, nBytes, 0); //(假设s时一个有效的socket连接)
很可能send()返回值ret小于2048字节。
该返回值时实际发送出去的字节数,因为系统为每一个socket分配了一个特定的缓冲区。
在TCP/IP中,我们知道收/发双方需要维护一个收/发窗口尺寸(window size),当接收方无法及时处理数据时,
会将发送窗口尺寸调小,即使发送方受到限制。假设发送方窗口尺寸被限制为1024字节,那么为了发送2048
字节的数据,必须分两次发送才能发送完。
下面的代码以流式协议确保你的数据被完整发送:
char sendbuff[2048]; //发送缓冲区int nBytes = 2048, //缓冲区大小 nLeft, //剩余未发送数据大小 idx; //发送偏移,即从idx标号的数据开始发送nLeft = nBytes;idx = 0;while (nLeft > 0) //每次发送ret字节后,继续发送剩余的数据{ ret = send(s, &sendbuff[idx], nLeft, 0); if (ret == SOCKET_ERROR) { // Error } nLeft -= ret; idx += ret;}
流式协议在接收数据上使用同样原则,但相对次要。
因为流式socket是一个持续的数据流,当一个应用读取数据时,并不关心它应该读多少数据。
如果你的应用想使用流式协议,接收一系列大小不等的数据,你将做些额外的操作。
若果所有的数据大小相等,将会方便很多,比如512字节大小的数据接收代码如下所示:
char recvbuff[1024];int ret, nLeft, idx;nLeft = 512;idx = 0;while (nLeft > 0){ ret = recv(s, &recvbuff[idx], nLeft, 0); if (ret == SOCKET_ERROR) { // Error } idx += ret; nLeft -= ret;}
当你的接收消息大小不一致时,将会变得略微复杂,你需要写一个你自己的协议来让接受者知道它将要接收多少数据量。
比如在数据包的最开始四个字节保存数据包大小,接受者收到后先解析这四个字节来得知还要接收多少数据。
分组-重组I/O(Scatter-Gather I/O)
分组-重组特性最开始在 Berkeley Sockets 中提出,它使用recv()和write()方法。这个特性对发送具有特定格式的数据很有用。
比如,客户端向服务端发送的消息经常是由32字节头、64字节数据块、16字节尾组成。
此时可以调用WSASend()并使用三个WSABUF结构体为元素组成的数组。
每一个元素包含三种数据类型,在接收端,WSARecv()被调用,并使用三个WSABUF结构体,
分别包含32字节、64字节、16字节的数据。
流式sockets(stream-based sockets),重组-分组操作将多个WSABUF当成一个连续缓冲来处理,即可能发送部分,同样地,接收方可能在未
完全接收完该数据就返回。
而在基于消息的sockets(message-based)中,将按指定大小接收整个消息。若接收缓冲区不够大,则返回WSAEMSGSIZE错误。
并且删除超出大小的数据。当然,支持MSG_PARTIAL可以防止数据丢失。
2 0
- Windows网络编程学习笔记(6) Socket关闭、流式协议、分组-重组I/O
- Windows Socket网络编程学习笔记一
- Java 网络编程---I/O部分学习笔记整理1
- Java网络编程---I/O部分学习笔记整理
- Java网络编程---I/O部分学习笔记整理
- Windows内核编程学习笔记“设备I/O”
- Windows内核编程学习笔记---设备I/O
- Unix网络编程学习笔记之第6章 I/O复用:select和poll函数
- I/O Socket编程
- Windows网络编程学习笔记(2) IPV4地址协议编程
- TCP/IP网络编程 基于Linux编程_2 --I/O流分离的半关闭问题
- Windows核心编程学习笔记(20)--同步设备I/O与异步设备I/O1
- Windows核心编程学习笔记(21)--同步设备I/O与异步设备I/O2
- UNIX环境编程学习笔记—文件I/O之标准I/O流
- Java 标准 I/O 流编程学习笔记(上)
- Java 标准 I/O 流编程学习笔记(下)
- 笔记,TCP协议socket网络编程
- Socket网络编程学习笔记
- 解决sql2005远程连接报错,提示请验证实例名称是否正确并且 SQL Server 已配置为允许远程连接
- 如何利用github搭建一个个人网站
- MVC里ActionResult或ViewResult跳转到当前控制器下对应的视图里的做法
- Runtime(二)成员变量与属性
- 关于 mysql 游标的初次体验
- Windows网络编程学习笔记(6) Socket关闭、流式协议、分组-重组I/O
- sql server实例内存使用统计
- RMQ (Range Minimum/Maximum Query)算法
- Android注解(Annotation)知识点总结整理
- #import、#include和@class有什么区别 ?
- 使用git遇到的坑
- 2016武汉科技大学邀请赛现场赛 A题
- 如何实现导航栏固定在某一位置不会随滚动条的移动而改变
- docker 私有 registry 透过 nginx 反向代理