与 127.0.0.1 上的 Putty 客户端连接时 C++ Winsock 库终止

C++ Winsock Library Termination When connecting with Putty Client on 127.0.0.1

我正在尝试创建一个服务器客户端,一旦它工作,我就可以将一个向量传递给它,并通过 ssh 将其发送到一个客户端程序,就像 putty 一样。问题是每当我尝试在 127.0.0.1:45000 上使用 putty 连接 raw 或 ssh 时,程序在连接后终止。

这是我的代码:

#include <iostream>
#include <WS2tcpip.h>
#include <string>

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

using namespace std;

void main()
{
    // Initialize winsock
    WSADATA wsData;
    WORD ver = MAKEWORD(2, 2);

    int wsOk = WSAStartup(ver, &wsData);
    if (wsOk != 0)
    {
        cerr << "Can't Intitialze winsock! Quiting" << endl;
        return;
    }

    // Create a socket to bind
    SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
    if (listening == INVALID_SOCKET)
    {
        cerr << "Can't create a socket! Quitting" << endl;
    }
    // Bind the socket to an ip address to the port
    sockaddr_in hint;
    hint.sin_family = AF_INET;
    hint.sin_port = htons(45000);
    hint.sin_addr.S_un.S_addr = INADDR_ANY; // could also use inet_pton

    bind(listening, (sockaddr*)&hint, sizeof(hint));

    // Tell winsock the socket is for listening
    listen(listening, SOMAXCONN);

    // Wait for connection
    sockaddr_in client;
    int clientSize = sizeof(client);

    SOCKET clientSocket = accept(listening, (sockaddr*)&client, &clientSize);

    char host[NI_MAXHOST];  //Clients remote name
    char service[NI_MAXHOST]; // Service (port) the client is on

    ZeroMemory(host, NI_MAXHOST);
    ZeroMemory(service, NI_MAXHOST); // use mem set of linux

    if (getnameinfo((sockaddr*)&client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
    {
        cout << host << " connected on port " << service << endl;
        return;
    }
    else
    {
        inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
        cout << host << " connected on port " <<
            ntohs(client.sin_port) << endl;
        return;
    }

    // Close listening socket
    closesocket(listening);
    // while loop; accept and echo message back to client
    char buf[4096];
    while (true)
    {
        ZeroMemory(buf, 4096);

        // wait for client to send data
        int bytesReceived = recv(clientSocket, buf, 4096, 0);
        if (bytesReceived == SOCKET_ERROR)
        {
            cerr << "Error in recv(). Quitting" << endl;
            break;
        }
        if (bytesReceived == 0)
        {
            cout << "Client Disconnected, bytes 0" << endl;
            break;
        }
        // echo message back to client
        send(clientSocket, buf, bytesReceived + 1, 0);

        // Close the socket

    }
    closesocket(clientSocket);


    // Shutdown winsock
    WSACleanup();
}

我正在 Visual Studio 2019 年编写和编译。

这是我在尝试使用 ssh 选项或原始连接时从 Putty 收到的消息。

如果有人能提供帮助,我们将不胜感激。谢谢!

调用 getnameinfo() 时,您会立即从 main() return'ing,而无需先调用 closesocket(),无论 getnameinfo() 是否成功或失败。这是您的 Putty 错误的根源。每当客户端连接时,您都会明确退出您的应用程序,而不通知客户端连接正在关闭。

更一般地说,如果 accept() 成功(并且 socket() 也成功),您应该 始终 在 returned SOCKET,无论您的代码中发生了什么(与 WSACleanup() 相同,如果 WSAStartup() 成功)。

您的代码中还有其他几个错误:

  • 非法 main() 具有非int return 类型(尽管 一些编译器允许这个,作为一个非标准的扩展。不要依赖这个!)。

  • 如果 socket() 失败,您缺少来自 main()return

  • 您没有检查 bind()listen()accept()send().

    上的错误
  • 如果您只去 accept() 1 个客户,那么将积压设置为 SOMAXCONN 毫无意义。

  • 您在调用 send() 时可能会发生缓冲区溢出。想象一下,如果 recv() returned 正好 收到 4096 个字节。将 bytesReceived + 1 字节数发送回客户端会超出 buf 数组的范围。

话虽如此,试试这样的东西:

#include <iostream>
#include <WS2tcpip.h>
#include <string>

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

using namespace std;

int main()
{
    // Initialize winsock
    WSADATA wsData;
    WORD ver = MAKEWORD(2, 2);

    int errCode = WSAStartup(ver, &wsData);
    if (errCode != 0)
    {
        cerr << "Can't initialize winsock! Error " << errCode << ". Quitting" << endl;
        return 0;
    }

    // Create a socket to bind
    SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
    if (listening == INVALID_SOCKET)
    {
        errCode = WSAGetLastError();
        cerr << "Can't create listening socket! Error " << errCode << ". Quitting" << endl;
        WSACleanup();
        return 0;
    }

    // Bind the socket to an ip address to the port
    sockaddr_in hint = {};
    hint.sin_family = AF_INET;
    hint.sin_port = htons(45000);
    hint.sin_addr.s_addr = INADDR_ANY; // could also use inet_pton

    if (bind(listening, (sockaddr*)&hint, sizeof(hint)) == SOCKET_ERROR)
    {
        errCode = WSAGetLastError();
        cerr << "Can't bind listening socket! Error " << errCode << ". Quitting" << endl;
        closesocket(listening);
        WSACleanup();
        return 0;
    }

    // Tell winsock the socket is for listening
    if (listen(listening, 1) == SOCKET_ERROR)
    {
        errCode = WSAGetLastError();
        cerr << "Can't open listening socket! Error " << errCode << ". Quitting" << endl;
        closesocket(listening);
        WSACleanup();
        return 0;
    }

    // Wait for connection
    sockaddr_in client;
    int clientSize = sizeof(client);

    SOCKET clientSocket = accept(listening, (sockaddr*)&client, &clientSize);
    if (clientSocket == INVALID_SOCKET)
    {
        errCode = WSAGetLastError();
        cerr << "Can't accept a client! Error " << errCode << ". Quitting" << endl;
        closesocket(listening);
        WSACleanup();
        return 0;
    }

    char host[NI_MAXHOST];  //Clients remote name
    char service[NI_MAXHOST]; // Service (port) the client is on

    ZeroMemory(host, NI_MAXHOST);
    ZeroMemory(service, NI_MAXHOST); // use mem set of linux

    if (getnameinfo((sockaddr*)&client, clientSize, host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
    {
        cout << host << " connected on port " << service << endl;
    }
    else
    {
        inet_ntop(AF_INET, &(client.sin_addr), host, NI_MAXHOST);
        cout << host << " connected on port " << ntohs(client.sin_port) << endl;
    }

    // Close listening socket
    closesocket(listening);
    listening = INVALID_SOCKET;

    // while loop; accept and echo message back to client
    char buf[4096];
    while (true)
    {
        // wait for client to send data
        int bytesReceived = recv(clientSocket, buf, sizeof(buf), 0);
        if (bytesReceived == SOCKET_ERROR)
        {
            errCode = WSAGetLastError();
            cerr << "Error reading from client: " << errCode << ". Quitting" << endl;
            break;
        }

        if (bytesReceived == 0)
        {
            cout << "Client Disconnected" << endl;
            break;
        }

        // echo message back to client
        char *ptr = buf;
        int bytesToSend = bytesReceived;
        do
        {
            int bytesSent = send(clientSocket, ptr, bytesToSend, 0);
            if (bytesSent == SOCKET_ERROR)
                break;
            ptr += bytesSent;
            bytesToSend -= bytesSent;
        }
        while (bytesToSend > 0);

        if (bytesToSend != 0)
        {
            errCode = WSAGetLastError();
            cerr << "Error writing to client: " << errCode << ". Quitting" << endl;
            break;
        }
    }

    // Close the client socket
    closesocket(clientSocket);

    // Shutdown winsock
    WSACleanup();

    return 0;
}