在《VS2015中基于TCP客户端的实现》和《VS2015中基于TCP服务端的实现》中介绍了服务端将本端套接字设置为监听模式,客户端连接服务端,服务端接收客户端的连接。此时,客户端和服务端的套接字已经成功连接,接下来就是通过连接好的套接字进行数据发送和接收了。
1 数据的发送
通过send()函数或者WSASend()函数可以实现发送数据。WSASend()函数是Winsock2中的函数。Winsock1和Winsock2的相关知识请参考《Winsock网络编程头文件及库文件的设置》。
1.1 send()函数
该函数的作用是通过已连接的套接字发送数据。
1.1.1 函数格式
send()函数的格式如下所示
int WSAAPI send(
SOCKET s
, const char *buf
, int len
, int flags
);
其中,参数s是已连接的套接字;buf是保存了发送数据缓冲区的指针,该缓冲区中保存的数据都是以一个字节为单位的窄字符;len是缓冲区buf的大小;flags指定了发送数据时的方法,一般有三个取值,其中0表示常规的发送数据,第二个可能的取值是MSG_DONTROUTE,最后一个值是MSG_OOB,其中MSG_DONTROUTE表示数据不会被路由,MSG_OOB表示发送OOB数据。如果send()函数执行成功,则该函数返回的是实际发送数据的字节数,否则返回SOCKET_ERROR。需要注意的是,send()函数执行成功并不意味着对端能够成功收到数据,只是意味着发送端成功将数据发送出去。
相关链接1:
OOB数据:OOB是out-of-band的缩写,其含义为带外数据。传输层协议使用带外数据来发送一些重要的数据。如果通信一方有重要的数据需要通知对方时,协议能够将这些数据快速地发送到对方。为了发送这些数据,协议一般不使用与普通数据相同的通道,而是使用另外的通道实现。
1.1.2 相关代码
在客户端中,通过以下代码实现向服务端发送数据的功能。
char *sendbuf = "Client: sending data test";
iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 );
if (iResult == SOCKET_ERROR)
{
wprintf(L"send failed with error: %d\n", WSAGetLastError()); closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %d\n", iResult);
其中,sendbuf指向的缓冲区中是要发送的数据;ConnectSocket是连接服务端的套接字。如果发送数据失败,则通过WSAGetLastError()函数获取失败的信息,并且调用closesocket()函数关闭套接字,调用WSACleanup()函数释放资源。最后打印出发送数据的字节数iResult。
1.2 WSASend()函数
WSASend()函数与send()函数的作用相同,也是在已连接的套接字上发送数据。
1.2.1 函数格式
该函数的格式为
int WSAAPI WSASend(
SOCKET s
, LPWSABUF lpBuffers
, DWORD dwBufferCount
, LPDWORD lpNumberOfBytesSent
, DWORD dwFlags
, LPWSAOVERLAPPED lpOverlapped
, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
其中,参数s是已经连接的套接字;lpBuffers是一个数组的指针,该数组中的元素是WSABUF结构的指针,每个WSABUF结构中包含了一个指向缓冲区的指针和该缓冲区的大小,这些缓冲区中包含了要发送的数据;dwBufferCount指定了lpBuffers数组中元素的个数;lpNumberOfBytesSend是实际发送数据的字节数,该变量是一个输出变量,与send()函数的返回值类似;dwFlags用来设置通过套接字发送数据时的方法,与send()函数的flags类似;lpOverlapped是一个WSAOVERLAPPED结构的指针,对于非重叠套接字,该参数可以忽略;lpCompletionRoutine是一个完成例程(completion routine)的指针,完成例程实际上是一个回调函数,当发送数据完成之后,该完成例程将会被调用,对于非重叠套接字,该参数可以忽略。如果调用WSASend()函数发送数据成功,则该函数返回值是0,否则返回值是SOCKET_ERROR。
相关链接2:
WSABUF结构:WSABUF结构实际上就是一个缓冲区的描述,它为一些Winsock函数提供了创建或者操作数据缓冲区的机会。WSABUF结构的格式为:
typedef struct _WSABUF {
ULONG len
; CHAR *buf;
} WSABUF, *LPWSABUF;
其中,len是缓冲区的大小,buf是缓冲区的指针。
1.2.2 相关代码
在客户端中,通过以下代码实现向服务端发送数据的功能。
#define DATA_BUFSIZE 4096
WSABUF DataBuf;
DWORD SendBytes;
DataBuf.len = DATA_BUFSIZE;
DataBuf.buf = “Client: sending data test”;
int rc = WSASend(
ConnectSocket
, &DataBuf
, 1
, &SendBytes
, 0
, NULL
, NULL);
if (rc == SOCKET_ERROR)
{
printf("WSASend failed with error: %d\n", err);
}
其中,WSASend()函数的参数ConnectSocket是连接服务端的套接字;DataBuf是WSABUF结构的对象,包含了要发送的内容;SendBytes中保存了实际向服务端发送的字节数。
1.2.3 WSASend()函数与send()函数的区别
从以上介绍可知,WSASend()函数与send()函数的作用都是通过已连结的套接字向对端发送数据。当发送的数据比较少的时候,两个函数的功能差不多。当发送的数据比较多时,需要多次调用send()函数才会实现,作为Winsock API函数的send(),实际上调用了相应的内核函数。多次调用send()函数会导致多次调用内核函数,这样会消耗大量时间。虽然WSASend()函数实际上也是调用了相应的内核函数,但是该函数可以与多个WSABUF相关连,WSABUF实际上就是一个保存数据的内容,当发送的数据较多时,系统先将要发送的数据保存到与WSASend()函数关连的多个WSABUF对象中,之后再调用WSASend()函数一起将数据进行发送,这样就减少了调用内核函数的次数,节约了时间。
1270

被折叠的 条评论
为什么被折叠?



