官方文档:https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-recv
1. 函数原型
recv函数从连接的套接字或绑定的无连接套接字接收数据。
int WSAAPI recv(
SOCKET s,//标识已连接套接字的描述符。
char *buf,//指向缓冲区的指针,以接收传入的数据。
int len,//buf参数指向的缓冲区的长度(以字节为单位)。
int flags//一组影响此功能行为的标志。
);
**本质:**复制
- 数据的接收都是由协议本身做的,也就是socket的底层做的,系统会有一段缓冲区,存储着接收到的数据。
- 咱们外边调用recv的作用,就是通过socket找到这个缓冲区,并把数据复制到咱们的
参数2
指向的地址,复制参数3
个。
2. 函数使用
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>
// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
int __cdecl main() {
//----------------------
// Declare and initialize variables.
WSADATA wsaData;
int iResult;
SOCKET ConnectSocket = INVALID_SOCKET;
struct sockaddr_in clientService;
char *sendbuf = "this is a test";
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
//----------------------
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != NO_ERROR) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
//----------------------
// Create a SOCKET for connecting to server
ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ConnectSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError() );
WSACleanup();
return 1;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" );
clientService.sin_port = htons( 27015 );
//----------------------
// Connect to server.
iResult = connect( ConnectSocket, (SOCKADDR*) &clientService, sizeof(clientService) );
if ( iResult == SOCKET_ERROR) {
closesocket (ConnectSocket);
printf("Unable to connect to server: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
// Send an initial buffer
iResult = send( ConnectSocket, sendbuf, (int)strlen(sendbuf), 0 );
if (iResult == SOCKET_ERROR) {
printf("send failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
printf("Bytes Sent: %ld\n", iResult);
// shutdown the connection since no more data will be sent
iResult = shutdown(ConnectSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
// Receive until the peer closes the connection
do {
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if ( iResult > 0 )
printf("Bytes received: %d\n", iResult);
else if ( iResult == 0 )
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
} while( iResult > 0 );
// cleanup
closesocket(ConnectSocket);
WSACleanup();
return 0;
}
3. 参数详解
-
s
- 客户端或服务端的socket,每个客户端对应唯一的socket
-
buf
- 客户端消息的存储空间,也就是个字符数组
- 一般为1500字节
- 网络传输的最大单元,1500字节,也就是客户端发过来的数据,一次最大就是1500字节,这是协议规定,这个数值也是根据很多情况,总结出来的最优值
- 所以客户端最多一组来1500字节,咱们这头1500读一次,就够了。
-
len
- 想要读取到字节个数,一般是参数2的字节数-1,把
\0
字符串结尾留出来
- 想要读取到字节个数,一般是参数2的字节数-1,把
-
flags
-
一般设置为0
-
MSG_PEEK:窥视传入的数据。 数据被复制到缓冲区中,但不会从输入队列中删除。
-
MSG_OOB:处理带外(OOB)数据。
-
MSG_WAITALL:
仅当发生以下事件之一时,接收请求才会完成:
- 调用方提供的缓冲区已完全满。
- 连接已关闭。
- 该请求已被取消或发生错误。
-
4. 返回值
- 读出来的字节大小
- 读没了咋办?
- 在recv函数卡着,等着客户端发来数据(即阻塞、同步)
- 阻塞的
- 读没了咋办?
- 客户端下线,返回0
- 释放客户端socket
- 执行失败,返回
SOCKET_ERROR
WSAGetLastError()
得到错误码- 根据错误码信息做相应处理
- 重启
- 等待
- 不用理会