send()或recv()不同步

时间:2017-05-15 16:05:42

标签: c++ networking winsock2

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

Server.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;
}

Client.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()电话一样。

2 个答案:

答案 0 :(得分:2)

在服务器端:

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

  • 您在c的每次通话中都没有重置accept(),并且您没有在接受的每个客户端上调用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);
    

答案 1 :(得分:0)

来自closesocket()的MSDN文档:

  

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

基本上,当您关闭套接字时,您发送的数据仍处于待处理状态。