关于socket send recv 两个函数各种错误说明

首先吐槽以下微软的socket的两个最重要的API:send函数和recv函数,本菜鸟感觉这两个函数是用来给程序员自己开发的函数,用起来跟翔一样的体验。

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

int send(
  _In_       SOCKET s,
  _In_ const char   *buf,
  _In_       int    len,
  _In_       int    flags
);

这是这两个函数的原型,参数含义我就不解释了,这里面常见的两种错误:

1、发送长度问题。这是个老生常谈的问题,在send一串字符的时候应该首先发送这串字符的长度n,然后才能在recv的时候recv(HANDLE , recvstring , n , 0);

2、还是发送长度的问题。在现实程序中发现一个问题,当发送长度过大时候,经常一次recv不够完全,需要多次recv才能接收完全,经常发现send 10000个字符,recv返回的接收字符串长度小于10000。这时候就应该这样写

int cSocket::SendBits(char * s , int bytes)
{
	int sendbytes = 0;
	while(sendbytes < bytes)  //实际发送长度小于应该发送长度</span>
	{
		sendbytes+= send( hSocket , (char *)(s+sendbytes) , bytes-sendbytes , 0 );
	}
	return sendbytes;
}

int cSocket::RecvBits(char *& s , int bytes)
{
	int recvbytes = 0;
	while(recvbytes < bytes)  //实际接收长度小于应该接收长度
	{
		recvbytes += recv( hSocket , s+recvbytes , bytes-recvbytes , 0 );
	}
	return recvbytes;
}
再来对比以下MSDN上面的例子

#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;
}
MSDN在处理recvbuf的时候任然没有判断iResult和recvbuflen之间的关系。为整个程序埋下了隐患。(其实在MSDN remark中有简单说明)真是坑啊。


  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
send函数recv函数是VC++Socket编程中最常用的两个函数,它们分别用于发送数据和接收数据。 send函数的原型如下: ```c++ int send( SOCKET s, const char* buf, int len, int flags ); ``` 参数说明: - s:需要发送数据的Socket。 - buf:指向包含要发送数据的缓冲区的指针。 - len:要发送的数据的字节数。 - flags:调用方式标志。 返回值说明: - 成功:返回实际发送的字节数。 - 失败:返回SOCKET_ERROR。 recv函数的原型如下: ```c++ int recv( SOCKET s, char* buf, int len, int flags ); ``` 参数说明: - s:需要接收数据的Socket。 - buf:指向接收数据的缓冲区的指针。 - len:要接收的数据的最大字节数。 - flags:调用方式标志。 返回值说明: - 成功:返回实际接收的字节数。 - 失败:返回SOCKET_ERROR。 sendrecv函数的使用方法如下: ```c++ char sendbuf[] = "Hello, world!"; int sendbuflen = sizeof(sendbuf); int sentbytes = 0; sentbytes = send(sock, sendbuf, sendbuflen, 0); if (sentbytes == SOCKET_ERROR) { // 发送失败 } char recvbuf[1024]; int recvbuflen = sizeof(recvbuf); int receivedbytes = 0; receivedbytes = recv(sock, recvbuf, recvbuflen, 0); if (receivedbytes == SOCKET_ERROR) { // 接收失败 } ``` 使用sendrecv函数时需要注意以下几点: - sendrecv函数都是阻塞式的,即程序会一直等待直到发送接收完所有数据。 - 如果发送接收的数据量比较大,需要多次调用sendrecv函数。 - 如果发送接收的数据量比较小,建议使用TCP_NODELAY选项关闭Nagle算法,以提高发送接收的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值