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

时间:2013-12-11 03:34:18

标签: c sockets

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

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字段修复了我的问题。

2 个答案:

答案 0 :(得分:6)

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

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); 

答案 1 :(得分:1)

read(sock, BUFF, 1024);

没有NULL终止缓冲区。你需要明确地做。它返回读取的字节数,并将许多字节复制到缓冲区。

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

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