客户端/服务器套接字通信 (AF_UNIX)

Posted

技术标签:

【中文标题】客户端/服务器套接字通信 (AF_UNIX)【英文标题】:Client/Server Socket Communication (AF_UNIX) 【发布时间】:2013-12-11 03:34:18 【问题描述】:

我正在尝试编写一个客户端程序和一个服务器程序,其中当客户端连接到服务器时,服务器将一个随机字符串从文件发送回它。这是我到目前为止所拥有的(从省略的文件中读取):

server.c

#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>

int listfd;
int connfd;

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

    /*
    * Create Sockets
    */
    listfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if(listfd == -1)
        exit(-1);

    struct sockaddr saddr = AF_UNIX, "server";
    socklen_t saddrlen = sizeof(struct sockaddr) + 6;
    bind(listfd, &saddr, saddrlen);

    listen(listfd, 10);

    fflush(stdout);
    printf("Running...\n");

    /*
    * Listen for connections
    * and send random phrase on accept
    */
    while(1)
        connfd = accept(listfd, NULL, NULL);

        int r = rand() % num_of_lines; //Pick random phrase/hint pair
        write(connfd, phrases[r], strlen(phrases[r]));

        close(connfd);
        sleep(1);
    

    exit(0);

client.c

#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ioctl.h>

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

    int sock;
    int conn;

    struct sockaddr saddr = AF_UNIX, "server";
    socklen_t saddrlen = sizeof(struct sockaddr) + 6;

    sock = socket(AF_UNIX, SOCK_STREAM, 0);

    conn = connect(sock, &saddr, saddrlen);

    char BUFF[1024];

    read(sock, BUFF, 1024);

    printf("%s", BUFF);

    return 0;

当我尝试在客户端打印时出现问题。我运行服务器,但是当我运行客户端时,它只打印垃圾字符,我不完全确定是什么原因造成的。

任何帮助将不胜感激,谢谢!

编辑:

我发现了我的问题。因为服务器套接字绑定到“服务器”,这也是可执行文件的名称,这引起了很多问题。

重命名 sockaddr.sa_data 字段解决了我的问题。

【问题讨论】:

至少...在接收数据前初始化BUFF。 尝试发送非常大的响应(>64 字节),看看会发生什么。 仔细阅读 read()/write() 的手册页并了解至少对于套接字,这两个函数不一定接收/发送尽可能多的字节,但很少。因此,循环计算此类调用直到所有数据都已接收/发送是一个好主意,而不是说必不可少。 @alk - 你从你的“standardTCPcockups.txt”文件中复制/粘贴? :) @MartinJames:在创建了几个基本相同的 cmets 之后,它收敛到了当前版本,即上面的那个。 【参考方案1】:

你有很多问题。这是错误的:

struct sockaddr saddr = AF_UNIX, "server";
socklen_t saddrlen = sizeof(struct sockaddr) + 6;

你告诉内核“这是一个代表套接字地址的字节包。它的长度是 sizeof(struct sockaddr) + 6 字节”,但你只传递了 sizeof(struct sockaddr) 字节。结果:内核越界读取您的地址,最多导致系统调用失败并显示EFAULT,最坏的情况是导致内核读入垃圾数据。

设置AF_UNIX 套接字地址的正确方法是使用用于AF_UNIX 套接字的套接字地址类型,即struct sockaddr_un(参见unix(7)):

struct sockaddr_un saddr = AF_UNIX, "/socket/path";
bind(listfd, (struct sockaddr *)&saddr, sizeof(saddr));

同样,调用connect(2)的客户端代码也应该以同样的方式设置地址。

接下来,您需要检查错误。这里几乎任何系统调用都可能失败,因此您需要在每次调用后检查失败并适当处理(通常关闭套接字和/或使用适当的错误消息终止进程)。

正如您所提到的,您还需要为套接字选择适当的路径名。命名为AF_UNIX 的套接字存在于文件系统中,因此不能与任何其他文件共享相同的名称,包括您的服务器和客户端程序。

您对read(2) 的调用需要使用返回值来确定读取的字节数。它不会以空值终止其输出——如果它读取 4 个字节,那 4 个字节将在您的缓冲区中,之后的所有其他内容都将是不确定的。由于printf 期望其输入以空值终止,因此您需要自己显式地以空值终止它,或者传入长度,例如:

int n = read(sock, BUFF, sizeof(BUFF));
if (n < 0)  /* Handle error */ 
printf("%.*s", n, BUFF); 

【讨论】:

【参考方案2】:
read(sock, BUFF, 1024);

不为 NULL 终止缓冲区。你需要明确地做到这一点。它返回读取的字节数并将许多字节复制到缓冲区。

 readlen = read(sock, BUFF, 1024);
 BUFF[readlen] = '\0';

检查connectread 的返回值是否有任何错误。检查服务器发送的内容。

【讨论】:

这只是部分答案。你不能指望一个完整的阅读,所以你真的需要循环,直到完整的消息被阅读,然后才空终止。 @R..:你是对的。客户端需要循环但要跟踪到目前为止已读取的内容以放置 NULL 字符。 在空终止符之前,它是否仍会打印字符,或者如果没有空终止符,printf 会阻止这种情况吗? printf 继续打印,直到出现 NULL 字符。如果没有 NULL 字符,它将继续搜索 NULL 字符和未定义的行为。 @alk:“空字符”是正确的 C 术语。 NUL 是 ASCII 术语。 NULL 是实现定义的空指针常量的宏标识符,我同意它在这种情况下的使用具有误导性。

以上是关于客户端/服务器套接字通信 (AF_UNIX)的主要内容,如果未能解决你的问题,请参考以下文章

AF_UNIX 套接字:我可以在进程之间传递套接字句柄吗?

一起talk C栗子吧(第一百五十七回:C语言实例--基于AF_UNIX域的数据报套接字通信)

一起talk C栗子吧(第一百五十六回:C语言实例--基于AF_UNIX域的流套接字通信)

基于socket简易版客户端,服务端交互

Socket

AF_UNIX 套接字路径中“\0hidden”的目的是啥?