如何使用套接字编程在两台远程机器之间发送数据?

How to send data between two remote machines using socket programming?

我正在尝试将数据从一台远程计算机 (Ubuntu) 发送到我的家用计算机 (High Sierra)。我读过 couple questions here at SO,但他们似乎没有解决我的问题。我使用ssh连接到远程计算机,并在两台计算机上创建并成功编译了以下程序。

client.cpp

#include <iostream>
#include <string>

#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>


int main(int argc, char* argv[])
{
    std::string address = argv[1];
    int port = 38473;
    int success;

    // http://man7.org/linux/man-pages/man2/socket.2.html
    int server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (server_socket == -1)
        return errno;

    sockaddr_in socket_address{};
    socket_address.sin_family = AF_INET;
    socket_address.sin_port = htons(port);  // htons - Host to Network Short. Flip endianness to match machine.
    success = inet_pton(AF_INET, address.c_str(), &socket_address.sin_addr);  // Pointer (to String) to Number.
    if (success <= 0)
        return errno;

    success = connect(server_socket, (sockaddr*)&socket_address, sizeof(socket_address));
    if (success == -1)
        return errno;

    std::cout << "Connected" << std::endl;
    for (int i = 0; i < 10; ++i)
        write(server_socket, "Hello!\n", 7);
}

server.cpp

#include <iostream>
#include <string>

#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>


int main(int argc, char* argv[])
{
    std::string address = "127.0.0.1";
    int port = 38473;
    int success;

    // http://man7.org/linux/man-pages/man2/socket.2.html
    int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_socket == -1)
        return errno;

    sockaddr_in socket_address{};
    socket_address.sin_family = AF_INET;
    socket_address.sin_port = htons(port);  // htons - Host to Network Short. Flip endianness to match machine.
    success = inet_pton(AF_INET, address.c_str(), &socket_address.sin_addr);  // Pointer (to String) to Number.
    if (success <= 0)
        return errno;

    // http://man7.org/linux/man-pages/man2/bind.2.html
    success = bind(listen_socket, (sockaddr*)&socket_address, sizeof(socket_address));
    if (success == -1)
        return errno;

    // http://man7.org/linux/man-pages/man2/listen.2.html
    success = listen(listen_socket, 10);
    if (success == -1)
        return errno;

    sockaddr_in client_address;
    socklen_t   client_address_size = sizeof(client_address);
    int client_socket = accept(listen_socket, (sockaddr*)&client_address, &client_address_size);
    if (client_socket == -1)
        return errno;

    close(listen_socket);

    size_t buffer_size = 4096;
    char buffer[buffer_size];
    ssize_t bytes_received;

    std::cout << "Connected" << std::endl;
    while (true)
    {
        memset(buffer, 0, buffer_size);
        // http://man7.org/linux/man-pages/man2/recvmsg.2.html

        if ((bytes_received = read(client_socket, buffer, buffer_size)) <= 0)
            return 0;
        std::cout << "Message: " << buffer << std::endl;
    }
}

当运行在同一台机器上运行两个程序时(客户端可执行文件在命令行中将 127.0.0.1 作为参数),它可以完美运行并发送数据。当尝试 运行 我的本地计算机作为服务器并从远程 运行ning client.cpp 时,我收到错误代码 111(连接被拒绝)。

看了各种tutorials/blogs/posts,好像是这样的:

  1. 端口当前未侦听。
  2. 防火墙正在阻止连接。

我已经尝试通过执行以下操作来解决此问题。

该端口当前未侦听。

由于远程机器正在尝试连接到我的 public IP 地址,我必须将一个端口转发到我的本地机器。我登录到我的路由器并添加了一个条目以将端口 38473 转发到我的私有 IP 地址。这个端口是 运行domly 选择的,因为 iana 已将其标记为未分配,而且它非常大。因为当 运行 在一台机器上同时运行两个程序时我可以发送数据,所以我假设端口不是问题。

防火墙正在阻止连接。

我已确保在本地计算机上禁用我的防火墙(在系统偏好设置 -> 安全和隐私 -> 防火墙中)并在我的路由器中创建一个条目以允许来自远程计算机的消息(使用其 public IP 地址)通过端口 38473.

我好像还是连接不上。我 运行 Wireshark and could see that the remote did send a SYN request, but got a [RST, ACK] in return. The RST flag 似乎是防火墙终止连接的一种方式,但考虑到我之前的上述配置,我不明白为什么它会这样做。

我做错了什么?

调整您的服务器代码以在计算机上侦听您感兴趣的以太网卡的 IP,或者:

   serv_addr.sin_family = AF_INET;
   serv_addr.sin_addr.s_addr = INADDR_ANY;
   serv_addr.sin_port = htons(portno);