【问题标题】:UNIX Socket Programming: Issues Getting a C Client to Connect to a Python ServerUNIX 套接字编程:让 C 客户端连接到 Python 服务器的问题
【发布时间】:2018-03-18 08:12:20
【问题描述】:

我正在尝试让 C 客户端连接到 UNIX/Linux 中的 Python 服务器。但是,我的 C 客户端似乎无法连接到 Python 服务器。我尝试在本地主机上运行,​​但这似乎不起作用。 将主机名硬编码到服务器和客户端(到“127.0.0.1”)也不起作用。

这是 Python 服务器的代码:

import socket
import sys

HOST = "127.0.0.1"
PORT = 8888

print("creating socket...")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print("binding socket...")
try:
    s.bind((HOST, PORT))
except socket.error as err:
    print("error binding socket, {}, {}".format(err[0], err[1]))
print("successfully bound socket")

s.listen(1)
print("now listening on {}:{}".format(HOST, PORT))

while True:
    conn, addr = s.accept()
    conn.sendall(str.encode("testing"))
    #conn.close()

s.close()

...这是 C 客户端的代码:

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

int main(int argc, char** argv) {
    if (argc != 3) {
        printf("usage: client [HOSTNAME] [PORT]\n");
        return 0;
    }

    const char *hostname = argv[1];
    int port = atoi(argv[2]);

    int sockfd = 0, n = 0;
    char recvBuff[1024];
    struct sockaddr_in saddr;

    memset(recvBuff, '0', sizeof(recvBuff));
    if ( sockfd = socket(AF_INET, SOCK_STREAM, 0) < 0 ) {
        fprintf(stderr, "error: could not create socket\n");
        return 1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_port   = htons(port);
    saddr.sin_addr.s_addr = htonl(hostname);

    if ( connect(sockfd, (struct sockaddr_in*)&saddr, sizeof(saddr)) < 0 ) {
        fprintf(stderr, "error: could not connect to %s:%i\n", hostname, port);
        return 1;
    }

    while ( n = read(sockfd, recvBuff, sizeof(recvBuff) - 1) > 0 ) {
        recvBuff[n] = 0;
        if ( fputs(recvBuff, stdout) == "EOF" ) {
            fprintf(stderr, "error: fputs\n");
        }

        printf("\n");
    }

    return 0;
}

有趣的是,我仍然可以通过其他方式连接到 Python 服务器,例如 telnet,或者通过这个用 Python 编写的客户端:

import socket
import sys

HOST = "localhost"
PORT = 8888

print("creating socket")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print("attempting to connect to host")
try:
    s.connect((HOST, PORT))
except socket.error as err:
    print("error: could not connect to host, {}, {}".format(err[0], err[1]))
    sys.exit()

print("successfully connected to host")

data = s.recv(1024)
s.close()

print("The server responded with: {}".format(data))

我认为这是一个客户端问题,因为其他连接到服务器的方法似乎工作得很好。如果是这种情况,我将如何修复 C 代码以正确连接?如果不是这种情况,这可能是与我的网络有关的问题吗?我尝试在 Arch VM 和我学校的 IT 服务器上运行,都产生了相同的结果。

【问题讨论】:

  • 当您的 C 客户端“不工作”时会发生什么
  • 您是否尝试过在调试器中运行您的客户端,看看它在做什么?
  • C 客户端连接失败。它在“错误:无法连接到 [主机]”处停止,然后简单地退出。我认为这意味着客户端根本无法连接到服务器。不,我还没有在调试器中运行客户端。

标签: python c linux sockets unix


【解决方案1】:

您的 C 客户端错误地将地址设置为(尝试)连接到。事实上,鉴于变量hostname 是一个指向程序参数之一的char *,这是完全错误的:

saddr.sin_addr.s_addr = htonl(hostname);

你的编译器应该发出警告;如果没有,那么要么调高警告级别,要么获得更好的编译器。 htonl() 需要一个 32 位无符号整数作为其参数。指针可以转换为该类型(虽然不是没有强制转换,但作为扩展除外),但这会转换指针本身,而不是它指向的数据。

您需要将提供的字符串形式的地址转换为数字形式,并将 that 分配给您的套接字地址。如果您愿意依赖“主机名”实际上被格式化为点分四线 IPv4 地址,您可以改为这样做:

int result = inet_pton(AF_INET, hostname, &saddr.sin_addr);

不要忽略检查返回值,并注意它采用了一个不寻常的约定:如果转换成功,您正在寻找它返回 1(而不是 0)。

如果您想接受域名以及点分四边形地址,那么您需要使用解析器来帮助您根据程序参数选择合适的目标地址。 getaddrinfo() 函数是您最好的入口,但仔细阅读(链接的)文档,因为有一些潜在的陷阱。

【讨论】:

    【解决方案2】:

    如果你查看htonl 的手册,它会说htonl(uint32_t hostlong);

    您将一个字符串作为参数传递,希望它被转换为正确的uint32 格式。这不会发生。

    你可以使用例如

    hostinfo = gethostbyname (hostname);
    saddr->sin_addr = *(struct in_addr *) hostinfo->h_addr;
    

    【讨论】:

    • +1 以获得准确的答案,但请注意gethostbyname() 的 Linux 手册页中的这些主要 cmets:“gethostbyname*() 和 gethostbyaddr*() 函数已过时。应用程序应改为使用 getaddrinfo(3) 和 getnameinfo(3)。"
    猜你喜欢
    • 2011-09-21
    • 2016-12-21
    • 1970-01-01
    • 1970-01-01
    • 2021-03-31
    • 1970-01-01
    • 2013-09-29
    • 2012-10-08
    • 1970-01-01
    相关资源
    最近更新 更多