BSD套接字不工作

Posted

技术标签:

【中文标题】BSD套接字不工作【英文标题】:BSD socket not working 【发布时间】:2012-01-28 06:03:28 【问题描述】:

更新:根据建议更新了 src。 strcat a \n\0 到命令并更改 sizeof。客户端在发送时“成功”,但服务器在接收时从不“成功”。

我付出了很多努力来尝试让一个常规的旧 TCP 套接字正常工作。我基本上有一个简单的客户端和服务器设置。目前我的服务器上什么都没有收到,我的客户端上只有 4 个字节(本例中为“GOTY”)。

服务器输出输出这个并继续:

RECEIVED (null)

客户端输出只是阻塞并且永不停止。如果我杀死服务器,我会得到这个输出

Connect success
Received Response:

服务器

int Server::update(char *getbuf)

    int ready = poll(pollfds, 1, 100);

    if (ready == -1)
    
        logger.info("Poll failed");
        return FALSE;
    

    if (pollfds[0].revents & POLLIN)
        
        ssize_t z;

        logger.info("Connection available");

        struct sockaddr_in client_address;
        memset(&client_address, 0, sizeof client_address);
        socklen_t alen = sizeof(client_address);

        // ACCEPT
        clientsocket = accept(serversocket, (struct sockaddr *)&client_address, &alen);
        if(!clientsocket)
            logger.fail("Accept Fail");

        // READ
        z = recv(clientsocket, getbuf, 512, MSG_WAITALL);
        if (z < 0)
            fprintf(stderr,"receive failure\n");
        else
            fprintf(stderr,"receive succeed\n");

        getbuf[z] = 0;

        respond(getbuf);

        //closeConnection();

        logger.data("RECEIVED %s", getbuf);

        getbuf[z] = 0;

        return TRUE;
    

    return FALSE;


void Server::respond(char *response)

    // SEND
    ssize_t z;

    z = send(clientsocket, response, strlen(response), 0);
    if (z < 0)
        fprintf(stderr,"send failure\n");
    else
        fprintf(stderr,"send succeed\n");

客户

我添加了客户端代码。我现在使用 strlen 但它似乎没有帮助。

int main(int argc, char **argv)

    int abort = 0;
    int port;
    in_addr_t address;
    char *command;

    ssize_t z;
    int com_socket;
    struct sockaddr_in server_address;
    struct timeval timeout;

    int opt;
    int val = 0;
    char getbuf[512];

    //Defaults
    address = inet_addr("127.0.0.1");
    port = 4949;

    //INCLUDED ARGUMENTS FROM CLI
    while((opt = getopt(argc, argv, "a:p:c:")) > 0) 
    
        switch(opt)
        
            case 'a':
                address = inet_addr(optarg);
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'c':
                abort = 1;
                command = (char *)optarg;
                break;   
            default:
                fprintf(stderr, "-a IPADDRESS -p PORT -c COMMAND\n",argv[0]);
        
    

    // Server
    memset(&server_address, 0, sizeof(server_address));
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(port);
    server_address.sin_addr.s_addr = address;

    if (server_address.sin_addr.s_addr == INADDR_NONE)
        fprintf(stderr, "Server address failed\n");

    if(!abort)
    
        fprintf(stderr, "No Command given\n");
        fprintf(stderr, "-a IPADDRESS -p PORT -C COMMAND\n");
        exit(0);
    
    else
    
        fprintf(stderr, "Address %s Port %d Command %s\n", inet_ntoa(server_address.sin_addr), port, command);
    

    // Create com_socket
    com_socket = socket(PF_INET, SOCK_STREAM, 0);
    if (com_socket == -1)
         fprintf(stderr, "Socket failed\n");

    /*
    // Client
    struct sockaddr_in client_address;
    memset(&client_address,0,sizeof client_address);
    client_address.sin_family = AF_INET;
    client_address.sin_port = 0;
    client_address.sin_addr.s_addr = ntohl(INADDR_ANY);

    if (client_address.sin_addr.s_addr == INADDR_NONE)
        fprintf(stderr, "Client address failed\n");

    // Bind
    z= bind(com_socket, (struct sockaddr *)&client_address, sizeof (client_address));
    if ( z == -1 )
        fprintf(stderr,"Binding port\n");
    */

    timeout.tv_sec = 2; /* 2 seconds */ 
    timeout.tv_usec = 0; /* + 0 usec */

    socklen_t addrlen = sizeof(struct sockaddr_in);

    // Connect
    //z = connectWithTimeout(com_socket, (struct sockaddr *) &server_address, len_inet, &timeout);
    z = connect(com_socket, (struct sockaddr *) &server_address, sizeof(server_address));
    if(z == -1)
    
        if(errno == EINPROGRESS)
        
            fprintf(stderr, "EINPROGRESS non block start\n");
        

        if(errno == EALREADY)
        
            fprintf(stderr, "EALREADY non block subsequent request\n");
        

        fprintf(stderr, "Connect failed\n");
    
    else 
        fprintf(stderr, "Connect success\n");

    strcat(command, "\n\0");

    // SEND
    z = send(com_socket, command, strlen(command)+2, 0);
    if (z < 0)
        fprintf(stderr,"send failure\n");
    else
        fprintf(stderr,"send succeed\n");

    // READ
    z = recv(com_socket, getbuf, 512, MSG_WAITALL);
    if (z < 0)
        fprintf(stderr,"receive failure\n");
    else
        fprintf(stderr,"receive succeed\n");

    // Output
    fprintf(stderr, "Received Response: %s\n", getbuf);

    close(com_socket);

    exit(1);

我只是不知道为什么这不起作用。我一次又一次地经历过。

这是一个嵌入式 Linux 系统。

【问题讨论】:

您没有显示实际调用send() 的函数。我建议在strace 下运行您的服务器和客户端,以查看它实际传递给send() 的数据和长度。 您是否首先尝试使用众多工具(特别是 gcc -Wall -ggdbvalgrindstrace ...)使您的代码在常规 Linux 桌面上运行?有货吗? 【参考方案1】:

在服务器中:这是完全错误的。 strlen() 返回缓冲区中字符串的长度 read() /recv() 之前。

    // READ
    z = recv(clientsocket, getbuf, strlen(getbuf), MSG_WAITALL);
    if (z < 0)
        fprintf(stderr,"receive failure\n");

另外:在 response() 中,strlen() 假定响应“字符串”是空终止的。不是。

z = send(clientsocket, response, strlen(response), 0);

客户端代码中似乎存在相同类型的错误。

更新:由于 OP 不知道如何在没有字符串函数的情况下生活,我将说明如何做到这一点。

 ... snipped ...
 // READ
    z = recv(clientsocket, getbuf, 512, MSG_WAITALL);
    if (z < 0) 
        // .... if (errno == EAGAIN) continue;
        fprintf(stderr,"receive failure %d(%s) \n"
             , errno, strerror(errno) );
        break; 

    fprintf(stderr,"receive succeed\n");

    respond(getbuf, z);

    return TRUE;
    
return FALSE;


void respond(char *response, size_t siz)

// SEND
size_t done;
ssize_t z;

for (done = 0; done < siz; done += z)  
    z = send(clientsocket, response+done, siz-done, 0);
    if (z < 0) 
        if (errno == EAGAIN) continue;
        fprintf(stderr,"send failure %d(%s)\n"
           , errno, strerror(errno) );
        break; 
    fprintf(stderr,"send succeed\n");
    

【讨论】:

那么我应该如何知道我收到的内容的长度?如果我输入缓冲区的大小,它会一直挂到它收到 256 个字节吗? 没有。它将读取当时可用的尽可能多的字节。您的程序应该记住它已经收集了多少数据,以及缓冲区中还剩下多少空间。 所以你确认我应该把缓冲区的大小放在这种情况下?因为它看起来好像仍然挂在接收端,值为 256(缓冲区大小)。 视情况而定。如果你有消息边界(例如命令行:以'\n'结尾的命令),阅读器应该积累数据直到它收集到一个完整的“命令”)如果它只是一个回显服务器(它似乎是) ,服务器可以在收到字节后回显它们。 (但它必须跟踪收到的金额,并且在回显时应该使用确切的金额。FWIW:即使 write() / send() 也可以发送部分数据:您应该检查它的返回值和采取相应的行动。 嗯,它是一个网络服务器,目前只是回显它接收到的内容以用于调试目的。如果我错了,请纠正我,但 TCP 数据包很容易适合我发送 【参考方案2】:

由于你实际上并没有分享你的“响应”功能的来源,我的精神力量告诉我它看起来像这样:

void Server::respond(char* str)

    send(clientsock, str, sizeof(str), 0);

因此,“sizeof(str)”的计算结果为 4,因为这是您机器上指针的大小。这与客户端接收“GOTY”(您打算发送的消息的前 4 个字节)一致。

将其更改为以下内容:

void Server::respond(char* str)

    send(clientsock, str, strlen(str), 0);

此外,您不应假设 send 和 recv 将返回一个等于您预期发送或接收的数据量的值。因此,您应该循环对 send/recv 的非阻塞调用,直到整个消息被发送或使用。

【讨论】:

如何知道消息何时完成? 你没有。您必须将传入的字符保存在缓冲区中并等到出现“\n”。 (或其他一些协议定义的语句终止符)【参考方案3】:

套接字上的发送操作通常会被缓冲,因此您的响应函数可能没有刷新所有数据。在respond() 的末尾尝试刷新操作以获取额外的数据。

在我看来,您正在看到阻塞行为,您可能想了解阻塞/非阻塞如何影响套接字。

【讨论】:

套接字没有缓冲;没有flush功能。 啊。我只使用过套接字库(如 python asyncore、luasocket 等),所以我猜他们添加了自己的缓冲区。我一直认为它是由操作系统完成的。

以上是关于BSD套接字不工作的主要内容,如果未能解决你的问题,请参考以下文章

CFNetwork 在 iOS 和 MacOS 上优于 BSD 套接字和 GCD 的优势?

围绕winsock的BSD套接字兼容包装器?

如何在 Solaris 上的 BSD 套接字上设置 TCP_NODELAY?

BSD socket API

Socket为啥要翻译成套接字?

浅析套接字中SO_REUSEPORT和SO_REUSEADDR的区别