Recv函数,如何有效使用它来接收网络数据?

recv函数用于从TCP连接的另一端接收数据,它等待发送缓冲中的数据被传送完毕,检查接收缓冲区并将数据复制到指定缓冲区。

recv函数详解

Recv函数,如何有效使用它来接收网络数据?

一、recv函数

recv函数是WinSock编程中用于从已连接套接字或数据报套接字接收数据的函数,其原型如下:

int recv( SOCKET s, char FAR* buf, int len, int flags);

参数说明

s:标识已连接的套接字的描述符。

buf:指向缓冲区的指针,该缓冲区用于存放recv函数接收到的数据。

len:缓冲区的长度(以字节为单位)。

flags:一组影响此函数行为的标志,通常置0。

返回值

成功时返回接收到的字节数。

如果连接已中止,则返回0。

出错时返回SOCKET_ERROR,可以通过调用WSAGetLastError()获取特定的错误代码。

二、recv函数的执行流程

当应用程序调用recv函数时,其执行流程如下:

1、等待发送缓冲区数据传送完毕:如果协议在传送发送缓冲中的数据时出现网络错误,recv函数将返回SOCKET_ERROR

2、检查接收缓冲区:如果没有数据或者协议正在接收数据,那么recv函数会一直等待,直到协议把数据接收完毕。

Recv函数,如何有效使用它来接收网络数据?

3、复制数据:一旦协议把数据接收完毕,recv函数就把接收缓冲中的数据复制到提供的缓冲区buf中,注意,协议接收到的数据可能大于缓冲区的大小,因此在这种情况下需要多次调用recv函数才能完全复制接收缓冲中的数据。

4、返回结果recv函数返回其实际复制的字节数,如果在复制过程中发生错误,则返回SOCKET_ERROR;如果在等待期间网络中断了,则返回0。

三、阻塞与非阻塞模式下的recv返回值

阻塞模式:默认情况下,socket是阻塞的,在阻塞模式下,recv函数会一直等待,直到有数据可读或者发生错误。

成功接收到数据时,返回接收到的字节数。

如果连接被远程计算机优雅地关闭了,返回0。

如果发生错误,返回SOCKET_ERROR。

非阻塞模式:在非阻塞模式下,recv函数的行为有所不同。

如果有数据可读,返回接收到的字节数。

如果没有数据可读,立即返回0,并设置WSAEWOULDBLOCK错误。

如果发生错误,返回SOCKET_ERROR。

四、常见错误代码及其含义

WSANOTINITIALISED:在使用此API之前,必须成功调用WSAStartup

WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。

WSAENOTCONN:套接口未连接。

Recv函数,如何有效使用它来接收网络数据?

WSAEINTR:阻塞进程被WSACancelBlockingCall()函数取消。

WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。

WSAENOTSOCK:描述字不是一个套接口。

WSAEOPNOTSUPP:指定了MSG_OOB,但套接口不是流样式套接口。

WSAESHUTDOWN:套接口已被关闭,当一个套接口以0或2的how参数调用shutdown()关闭后,无法再用recv()接收数据。

WSAEWOULDBLOCK:套接口标识为非阻塞模式,但接收操作会产生阻塞。

WSAEMSGSIZE:数据报太大无法全部装入缓冲区,故被剪切。

WSAEINVAL:套接口未用bind()进行捆绑。

WSAECONNABORTED:由于超时或其他原因,虚电路失效。

WSAECONNRESET:远端强制中止了虚电路。

五、示例代码

以下是一个简单的示例代码,演示如何使用recv函数从服务器接收数据:

#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <string.h>
// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
int main() {
    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
", 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
", 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
", WSAGetLastError());
        WSACleanup();
        return 1;
    }
    // Send an initial buffer
    iResult = send(ConnectSocket, sendbuf, (int)strlen(sendbuf), 0);
    if (iResult == SOCKET_ERROR) {
        printf("Send failed: %d
", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    printf("Bytes Sent: %ld
", iResult);
    // Shutdown the connection since no more data will be sent
    iResult = shutdown(ConnectSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("Shutdown failed: %d
", WSAGetLastError());
        closesocket(ConnectSocket);
        WSACleanup();
        return 1;
    }
    // Receive until the peer closes the connection
    do {
        iResult = recv(ConnectSocket, recvbuf, sizeof(recvbuf), 0);
        if (iResult > 0) {
            printf("Bytes received: %d
", iResult);
            printf("Data: %s
", recvbuf);
        } else if (iResult == 0) {
            printf("Connection closed
");
        } else {
            printf("recv failed: %d
", WSAGetLastError());
        }
    } while (iResult > 0);
    // Cleanup
    closesocket(ConnectSocket);
    WSACleanup();
    return 0;
}

上述代码展示了如何创建一个套接字,连接到服务器,发送数据,然后接收服务器响应的数据,通过循环调用recv函数,可以持续接收数据直到连接关闭。

原创文章,作者:未希,如若转载,请注明出处:https://www.kdun.com/ask/1271032.html

本网站发布或转载的文章及图片均来自网络,其原创性以及文中表达的观点和判断不代表本网站。如有问题,请联系客服处理。

(0)
未希新媒体运营
上一篇 2024-11-08 10:22
下一篇 2024-11-08 10:25

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

产品购买 QQ咨询 微信咨询 SEO优化
分享本页
返回顶部
云产品限时秒杀。精选云产品高防服务器,20M大带宽限量抢购 >>点击进入