客户端/服务器套接字通信 (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';
检查connect
和read
的返回值是否有任何错误。检查服务器发送的内容。
【讨论】:
这只是部分答案。你不能指望一个完整的阅读,所以你真的需要循环,直到完整的消息被阅读,然后才空终止。 @R..:你是对的。客户端需要循环但要跟踪到目前为止已读取的内容以放置 NULL 字符。 在空终止符之前,它是否仍会打印字符,或者如果没有空终止符,printf 会阻止这种情况吗?printf
继续打印,直到出现 NULL 字符。如果没有 NULL 字符,它将继续搜索 NULL 字符和未定义的行为。
@alk:“空字符”是正确的 C 术语。 NUL
是 ASCII 术语。 NULL
是实现定义的空指针常量的宏标识符,我同意它在这种情况下的使用具有误导性。以上是关于客户端/服务器套接字通信 (AF_UNIX)的主要内容,如果未能解决你的问题,请参考以下文章
一起talk C栗子吧(第一百五十七回:C语言实例--基于AF_UNIX域的数据报套接字通信)