C 套接字服务器和 Python 套接字客户端“资源暂时不可用”

Posted

技术标签:

【中文标题】C 套接字服务器和 Python 套接字客户端“资源暂时不可用”【英文标题】:C socket server and Python socket client "Resource temporarily unavailable" 【发布时间】:2022-01-01 11:41:19 【问题描述】:

我正在为 Ubuntu 18.04 上的 UNIX 域数据报套接字 (UDS) IPC 创建 C 服务器和 Python 客户端。我的场景是:Python 作为使用 fork-execv 创建的子进程运行,其中 C 是父进程。 Python 客户端在 socket.recvfrom 上阻塞,直到 C 服务器发送数据。当 C 服务器向 Python 客户端发送数据时,C 将阻塞 recvfrom,直到 Python 使用 sendto 向 C 发送数据。

我已经将 UDS 用于 C 客户端和 C 服务器,没有任何问题,但是 C-Python 设置导致了一些问题。对于这个 Python 版本,我使用了 https://lloydrochester.com/post/c/unix-domain-socket-datagram 的示例。

我在 C 中创建了一个服务器套接字并绑定到它;它返回文件描述符 5:

int64_t * create_socket_server(struct sockaddr_un svaddr, int64_t retvals[])

    int sfd, j;
    ssize_t numBytes;
    socklen_t len;
    char buf[BUF_SIZE];

    retvals[0] = 0;
    retvals[1] = 0;

    sfd = socket(AF_UNIX, SOCK_DGRAM, 0); /* Create server socket 

    if (sfd == -1)
         return retvals;

    if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
           return retvals;

    memset(&svaddr, 0, sizeof(struct sockaddr_un));
    svaddr.sun_family = AF_UNIX;
    strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);

    if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) == -1)
           return retvals;

    retvals[0] = sfd;
    retvals[1] = (int64_t)&svaddr;

     return retvals;

我没有在 C 端创建或显式连接到客户端套接字。

在 Python 端,我绑定到客户端套接字。这是我的 Python 代码,遵循引用的示例,但有所改动以适合我的用例:

#!/usr/bin/python3
import socket
import os, os.path

csock_file = "/tmp/py_sock"
ssock_file = "/tmp/ud_ucase"

if os.path.exists(csock_file):
  os.remove(csock_file)

csock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
csock.bind(csock_file) # Bind to the server socket

return_msg = "Got it"

while True:

    (bytes, address) = csock.recvfrom(720)
    msg = bytes.decode('utf-8')

    print("Python received")

    if msg != "Code_99":
        print('address:',address,'received:',msg)
        csock.sendto(str.encode(return_msg), ssock_file)

    if msg == "Code_99":
        print("closing")
        #Close the socket

我希望 recvfrom 在 Python 和 C 中都被阻塞,因为 Python 应该阻塞直到 C 发送,但是当我让它处于阻塞状态(默认)时,Python 在调用 (bytes, address) = csock.recvfrom 时阻塞这两个进程(720),C 无法继续。

如果我使用 csock.setblocking(False) 将其设置为非阻塞,我会收到以下错误消息:

(bytes, address) = csock.recvfrom(720)
BlockingIOError: [Errno 11] Resource temporarily unavailable

所以我的问题是为什么 Python 会阻塞这两个进程,为什么我会在非阻塞模式下收到该错误消息?

感谢您的帮助。

【问题讨论】:

你可以设置一个超时socket.settimeout(value)如果没有接收到它会抛出socket.error异常,你可以使用try和catch来实现 你知道为什么它会在阻塞模式下阻塞两个进程吗?我以为只有 Python 会阻塞。 我没有看到向客户端发送响应的代码,你能分享一下,以便我找出问题所在 在我调用 (bytes, address) = csock.recvfrom(720) 的地方还没有发送数据,因为我们刚刚连接。 Python连接并调用recvfrom后,服务器会发送数据。 【参考方案1】:

说明

为什么 Python 会阻止这两个进程?

当您的客户端正在等待您的服务器对recvfrom 的响应时,您的服务器什么也没做,因此服务器也会在其recvfrom 处阻塞。

为什么我在非阻塞模式下会收到该错误消息?

您的服务器/客户端可能不像您引用的那样强大(即来自 lloydrochester.com)。几个零件坏了,导致整个东西坏了。其中一些只是关于 C 语言的,例如 Variable DeclarationsFunction Returning 等。还有一些是关于网络编程的,例如 Buffer SizingSocket Internals等。全部列出来一一分析是不现实的。最好通读 K&R 和 BSD socket 以彻底修复它们。

但是,根据您的代码,这是一个相对简单的实现,如下所示。此外,您可能希望在 server_alice.c 的第 48 行将回复消息更改为 Code_99


环境

Ubuntu 18.04
gcc 7.5.0
Python 3.6.9

server_alice.c

#include <sys/un.h>
#include <sys/socket.h>
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#define BUF_SIZE 720
#define SV_SOCK_PATH "ssock"
int create_socket_server(int *sfd_ptr, struct sockaddr_un *svaddr_ptr);

int main(int argc, char *argv[]) 

    struct sockaddr_un svaddr, claddr;
    int sfd, j;
    ssize_t numBytes;
    socklen_t len;
    char buf[BUF_SIZE];

    int64_t retvals[2];

    if (create_socket_server(&sfd, &svaddr) == 0)
        printf("create_socket_server...DONE\n");
    else exit(0);

    for (;;) 
        len = sizeof(struct sockaddr);
        printf("waiting clients...\n");
        numBytes = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len);

        if (numBytes == -1) 
            fprintf(stderr, "error recvfrom");
            return 4;
        
        claddr.sun_path[len - sizeof(sa_family_t) - 1] = 0;
        buf[numBytes] = '\0';
        fprintf(stdout, "server received %ld bytes from %s, they are: \x1b[32m%s\x1b[0m\n", (long) numBytes,
                claddr.sun_path, buf);

        for (j = 0; j < numBytes; j++) 
            buf[j] = toupper((unsigned char) buf[j]);
        

        // char *reply_msg="Code_99";        #   different reply message
        char *reply_msg = "Hello Bob~ This is a message: blablablabla";

        j = sendto(sfd, reply_msg, strlen(reply_msg), 0, (struct sockaddr *) &claddr, len);
        if (j != strlen(reply_msg)) 
            fprintf(stderr, "error sendto %s", strerror(errno));
        
    
    exit(EXIT_SUCCESS);


/* Your create_socket_server, with a few changes */
int create_socket_server(int *sfd_ptr, struct sockaddr_un *svaddr_ptr) 
    struct sockaddr_un svaddr;
    int sfd = socket(AF_UNIX, SOCK_DGRAM, 0); // Create server socket

    if (sfd == -1)
        return -1;

    if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
        return -1;

    memset(&svaddr, 0, sizeof(struct sockaddr_un));
    svaddr.sun_family = AF_UNIX;
    strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);
    if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) == -1)
        return -1;

    memcpy(sfd_ptr, &sfd, sizeof(int));
    memcpy(svaddr_ptr, &svaddr, sizeof(struct sockaddr_un));


    return 0;

client_bob.py

#!/usr/bin/python3
import socket
import os, os.path

csock_file = "./csock"
ssock_file = "./ssock"

if os.path.exists(csock_file):
    os.remove(csock_file)

csock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
csock.bind(csock_file)  # Bind to the server socket

return_msg = "Got it"
csock.sendto(str.encode("Hello Alice! I'm coming!"), ssock_file)
# while True:     #       ! CATION !       If 'while(true)', there will be infinite message sending back and forth!
(bytes, address) = csock.recvfrom(720)
msg = bytes.decode('utf-8')

if msg != "Code_99":
    print('address:    ', address, 'received:   ', msg)
    csock.sendto(str.encode(return_msg), ssock_file)

if msg == "Code_99":
    print("closing")
    csock.close()

服务器输出:

$ gcc server_alice.c && ./a.out
create_socket_server...DONE
waiting clients...
server received 24 bytes from ./csock, they are: Hello Alice! I'm coming!
waiting clients...
server received 6 bytes from ./csock, they are: Got it
waiting clients...

客户端输出:

$ python3 client_bob.py 
address:     ssock received:    Hello Bob~ This is a message: blablablabla

【讨论】:

Michael - 在“numBytes = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) &claddr, &len); 行中你还没有识别出 &claddr。这个程序是如何只用一个指针就知道 claddr 结构的? @RTC222 这是设置变量值的一种很常见的方法,它是通过内存操作来完成的,比如memcpy。您可以看到它们出现在许多内置的 C 库函数中。或者你可以试试这个小代码:void set_val_using_ptr(int *i_need_new_val) int new_val=567; memcpy(i_need_val, &amp;new_val, sizeof(int)); ,也许会给你一个相当直观的解释。

以上是关于C 套接字服务器和 Python 套接字客户端“资源暂时不可用”的主要内容,如果未能解决你的问题,请参考以下文章

从客户端C发送套接字,服务器Python问题

Python 3套接字客户端发送数据和C++套接字服务器接收偏移数据?

Python套接字客户端:发送数据和接收结果

python之socket(套接字)

Python学习之——Socket套接字(TCP连接)

python----socket编程