【问题标题】:send() or recv() not syncing upsend() 或 recv() 未同步
【发布时间】:2017-05-15 16:05:42
【问题描述】:

我一直在做Winsock tutorials 并完全遵循它。我似乎无法让send() 或recv() 正常运行。我设置了一个基本的服务器和客户端程序,并且正在建立连接,但服务器没有向客户端发送消息。使用 Telnet 客户端也不会收到来自服务器的响应。我不太确定发生了什么,我看到的所有问题都不是基本的,或者有一些我无法真正理解他们在做什么的东西。

服务器.cpp

#include<WinSock2.h>
#include <io.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")  //winsock library

int main(int argc, char *argv[])
{
    WSADATA wsa;
    SOCKET s, new_socket;
    sockaddr_in server, client;
    int c;
    char *message;

    printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2,2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }
    else
        printf("Initialised.\n");

    //create a socket
    if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d", WSAGetLastError());
        return 2;
    }
    else
        printf("Socket created.\n");

    //prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.S_un.S_addr = INADDR_ANY;
    server.sin_port = htons(8888);

    //bind the socket
    if (bind(s, (sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed with error code : &d", WSAGetLastError());
        return 3;
    }
    else
        puts("Bind done");

    //listen
    listen(s, 3);

    //accept an incoming connection
    puts("Waiting for incoming connections...");

    c = sizeof(sockaddr_in);

    while (new_socket = accept(s, (sockaddr *)&client, &c) != INVALID_SOCKET)
    {
        printf("Connect successful...\n");

        //reply to the client
        message = "Hello Client, I have recieved your connection, but I have to go now, bye!\n";
        send(new_socket, message, strlen(message), 0);

        puts("Message sent.\n");
    }

    if (new_socket == INVALID_SOCKET)
    {
        printf("accept() failed with error code : %d", WSAGetLastError());
        return 4;
    }

    //close the socket
    closesocket(s);
    WSACleanup();

    return 0;
}

客户端.cpp

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <IPHlpApi.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

int main(int argc, char *argv[])
{
    //intialize variables
    WSADATA wsa;
    char ip[100] = "192.168.1.117";
    SOCKET s;
    sockaddr_in server;
    char *message, server_reply[75];
    int recv_size;

    //initialize Winsock
    printf("\nInitialising Winsock...\n");
    if(WSAStartup(MAKEWORD(2,2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }
    printf("Initialised.\n");

    //create the socket
    if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d", WSAGetLastError());
        return 3;
    }
    printf("Socket created.\n");

    server.sin_addr.S_un.S_addr = inet_addr(ip);
    server.sin_family = AF_INET;
    server.sin_port = htons(8888);

    //connect to the server
    if (connect(s, (sockaddr *)&server, sizeof(server)) < 0)
    {
        puts("connect error");
        return 4;
    }
    else
    {
        printf("Connect successful");

        recv_size = recv(s, server_reply, 75, 0);
    }

    if (recv_size <= 0)
    {
        puts("recv() failed\n");
    }
    else
    {
        //add a NULL terminating character to make it a proper string before printing
        server_reply[recv_size] = '\0';
        puts(server_reply);
    }

    getchar();

    //close the socket
    closesocket(s);
    WSACleanup();

    return 0;
}

客户端也无法打印"recv() failed" 行;就像它卡在recv() 通话中一样。

【问题讨论】:

  • 你必须深入挖掘,而不是“它失败”“它不能正常工作”。您可以运行 Wireshark 并检查网络流量。我认为您的数据包在某处被阻止。您在防火墙中打开了 8888 端口吗?
  • 我会先处理 send 和 recv 的错误情况,然后在调试器中检查它们。
  • 你应该检查send()是否失败:if( send(...) &lt; 0){...}
  • 谢谢你们的帮助。因为本教程没有错误检查 send()、recv() 函数,我并不认为你真的需要这样做。他们甚至说错误检查是你的朋友。将来我必须下载 Wireshark。在对我选择的答案进行了几处更改后,例如 do-while 循环、更多错误检查以及更改接收到的消息的输出显示方式,他们设法发送和接收数据包。

标签: c++ networking winsock2


【解决方案1】:

在服务器端:

  • 您没有检查listen() 的返回值是否有错误。

  • 您不会在每次调用 accept() 时重置 c,也不会在每个被接受的客户端上调用 closesocket()。

  • 您没有检查send() 的返回值是否有错误。

试试这个:

#include <WinSock2.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")  //winsock library

int main(int argc, char *argv[])
{
    WSADATA wsa;
    SOCKET s, new_socket;
    sockaddr_in server, client;
    int c, res, messagelen;
    const char *message;

    printf("\nInitializing Winsock...");
    res = WSAStartup(MAKEWORD(2,2), &wsa);
    if (res != 0)
    {
        printf("Failed. Error: %d\n", res);
        return 1;
    }
    printf("Initialized.\n");

    //create a socket
    if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        printf("Could not create socket. Error: %d\n", WSAGetLastError());
        return 2;
    }
    printf("Socket created.\n");

    //prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.S_un.S_addr = INADDR_ANY;
    server.sin_port = htons(8888);

    //bind the socket
    if (bind(s, (sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed. Error: &d", WSAGetLastError());
        return 3;
    }
    printf("Bind done.\n");

    //listen
    if (listen(s, 3) == SOCKET_ERROR)
    {
        printf("Listen failed. Error: &d", WSAGetLastError());
        return 4;
    }
    printf("Listening.\n");

    //accept incoming connections
    printf("Waiting for incoming connections...\n");

    do
    {
        c = sizeof(sockaddr_in);

        new_socket = accept(s, (sockaddr *)&client, &c);
        if (new_socket == INVALID_SOCKET)
        {
            printf("Failed to accept a client. Error: %d\n", WSAGetLastError());
            return 5;
        }

        printf("Client connected...\n");

        //reply to the client
        message = "Hello Client, I have received your connection, but I have to go now, bye!\n";
        messagelen = strlen(message);

        do
        {
            res = send(new_socket, message, messagelen, 0);
            if (res == SOCKET_ERROR)
            {
                printf("Failed to send message. Error: %d\n", WSAGetLastError());
                break;
            }

            message += res;
            messagelen -= res;
        }
        while (messagelen > 0);

        if (messagelen == 0)
            printf("Message sent.\n");

        //close the client socket
        closesocket(new_socket);
    }
    while (true);

    //close the server socket
    closesocket(s);
    WSACleanup();

    return 0;
}

在客户端,我看到的唯一真正的问题是您的 recv() 调用有潜在的缓冲区溢出等待发生,因为您要求它读取 75 个字节,而这正是您的缓冲区的大小。碰巧您的服务器最多只发送 74 个字节,但如果它发送更多,则在向其附加 '\0' 终止符时可能会溢出缓冲区。

所以,要么:

  • 调用recv() 并使用sizeof(server_reply)-1 作为缓冲区大小,为添加的终止符留出空间:

    recv_size = recv(s, server_reply, sizeof(server_reply)-1, 0);
    
  • 使用printf() 而不是puts(),因此在将缓冲区打印到控制台时根本不需要空终止缓冲区。您可以将recv_size 作为参数传递来限制正在打印的文本数量:

    //server_reply[recv_size] = '\0';
    //puts(server_reply);
    printf("%.*s", recv_size, server_reply);
    

【讨论】:

  • 我并没有真正考虑过检查 listen() 是否失败,因为教程也没有检查该错误或 send()/recv() 是否有错误。我也从未使用过 do-while 循环,也从未真正确定它们何时相关。我注意到的一件事是第一个“do”没有相应的“while”,所以我只是删除了它。我应该在那个 do-while 循环中设置什么条件,以便它在发送数据后不会关闭?在“if (listen(s, 3) == SOCKET_ERROR”中使用变量代替3,然后检查连接数没有超过这个数字好吗?
  • @L.Moyer 总是检查返回值,你永远不知道什么时候会出错。你对丢失的while 是正确的,我现在添加了它。
  • 我会确保从现在开始这样做。谢谢!
【解决方案2】:

来自关于 closesocket() 的 MSDN 文档:

注意 为确保在连接上发送和接收所有数据,应用程序应在调用 closesocket 之前调用 shutdown(有关详细信息,请参阅优雅关闭、延迟选项和套接字关闭)。另请注意,调用 closesocket 后不会发布 FD_CLOSE 网络事件。

基本上你发送的数据在你关闭套接字时仍然处于等待状态。

【讨论】:

  • closesocket() 就足够了。 MSDN 正在讨论您希望确保所有数据都发送和接收的罕见情况,这可以通过双方关闭然后读取到流的末尾来完成。这不仅仅是为了确保发送所有数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-08-02
  • 2014-05-05
  • 1970-01-01
  • 2019-01-13
  • 1970-01-01
  • 2020-01-08
  • 2016-07-27
相关资源
最近更新 更多