我在AF_UNIX
套接字通信中遇到了一些问题,因为在写完数据缓冲区之后,似乎还有一些挂起的字节要读取,我不知道它们来自何处。
我正在用C编写一个多线程服务器程序,该程序通过AF_UNIX
套接字与客户端通信,它必须实现一个简单的聊天室。除其他外,服务器必须在客户端和服务器之间实现文件传输,当我尝试从服务器向客户端发送相当大的文件(269K)时遇到了问题。 (使用较小的文件,我没有任何问题)
对于文件传输,我使用mmap()
函数,该函数返回指向要发送的文件的映射的指针,然后使用write()
在与必须接收的客户端链接的套接字上写入该数据文件。
在write()
调用之后,我检查返回的值是否等于文件大小。 (始终已验证)
客户端在接收到文件之后,检查读取数据的大小(始终经过验证)并开始等待其他消息,以便它调用阻塞read()
。 这是我发现错误的地方,因为客户端读取了本不应该存在的内容,好像套接字上还有一些要读取的内容。
我已经调试了这部分(服务器和客户端)两天了,但我还无法理解问题的根源。
我确定没有其他线程同时在同一套接字上写入
你们中的任何一个都知道导致此错误的原因是什么吗?
我尝试按照正常的操作顺序发布一些有用的代码思考:
首先是消息结构:
struct message_hdr
{
op_t op;
char sender[MAX_NAME_LENGTH+1];
};
struct message_data_hdr{
char receiver[MAX_NAME_LENGTH+1];
unsigned int len;
};
struct message_data
{
message_data_hdr_t hdr;
char *buf;
};
struct message
{
message_hdr_t hdr;
message_data_t data;
};
服务器到客户端的文件传输始于服务器,服务器向正在等待message_hdr_t
的客户端发送read()
(客户端希望仅接收message_hdr_t
)。 / p>
int sendHeader(long fd, message_hdr_t* hdr)
{
if(hdr == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
struct iovec iov;
iov.iov_base = hdr;
iov.iov_len = sizeof(message_hdr_t);
test = writev(fd, &iov, 1);
return test;
}
客户端从操作代码(message.hdr.op
)得知这是文件类型消息,并且开始等待文件
服务器将其发送:
int sendData(long fd, message_data_t *msg)
{
if(msg == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
struct iovec iov;
iov.iov_base = &(msg->hdr);
iov.iov_len = sizeof(message_data_hdr_t);
test = writev(fd, &(iov), 1);
if(test == -1){return -1;}
if (msg->hdr.len != 0)
{
test = write(fd, msg->buf, msg->hdr.len);
if(test <= 0)
return -1;
}
return test;
}
客户阅读:
int readData(long fd, message_data_t *data)
{
if(data == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
struct iovec iov;
iov.iov_base = &(data->hdr);
iov.iov_len = sizeof(message_data_hdr_t);
test = readv(fd, &iov, 1);
if(test <= 0){return -1;}
if(data->hdr.len != 0)
{
data->buf = malloc(data->hdr.len);
if(data->buf == NULL){return -1;}
test = read(fd, data->buf, data->hdr.len);
if((unsigned int)test != data->hdr.len)
return -1;
}
return test;
}
此时,客户端接收到文件,并重新启动以等待新消息:
int readMsg(long fd, message_t *msg)
{
if(msg == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
test = readHeader(fd, &(msg->hdr));
if(test == -1 || test == 0){return -1;}
test += readData(fd, &(msg->data));
return test;
}
这是客户应该等待的地方,因为没有收入消息,在这种情况下,它会读取一些我不知道消息来源的消息。
当我尝试使用GDB打印此不需要的消息时,它会打印:
{hdr = {op = 512,
sender = "\000\000\020G\032\324\t\000\000\n\000\000\000\000\030\021B\bC\n\000\000\v\000\000\000\000\021D\v\222\000"},
data = {hdr = {receiver = "\000\000\000\000\021E\022C\n\000\000\b\v\000\000\000\000\021F\020I\n\000\000\020\000\006\b\002\n\000\000\006",
len = 131072},
buf = 0x7ffff7f2f010 ""}`
这当然是没有意义的。
我希望这个描述会有用
谢谢大家。
答案 0 :(得分:-1)
好,我解决了我的问题。 如评论中所述,此问题是由于缺少对部分写作的检查所致。
现在函数readData()
如下所示:
int readData(long fd, message_data_t *data)
{
if(data == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
char* ph;
unsigned int rd = 0;
struct iovec iov;
iov.iov_base = &(data->hdr);
iov.iov_len = sizeof(message_data_hdr_t);
test = readv(fd, &iov, 1);
if(test <= 0){return -1;}
if(data->hdr.len != 0)
{
data->buf = malloc(data->hdr.len);
if(data->buf == NULL){return -1;}
ph = data->buf;
while (rd < data->hdr.len)
{
test = read(fd, ph, data->hdr.len - rd);
if(test == -1)
return -1;
else if(test == 0)
{
errno = ENOENT;
return -1;
}
rd += test;
ph += test;
}
}
return rd;
}
和sendData()
:
int sendData(long fd, message_data_t *msg)
{
if(msg == NULL || fd < 0) {errno = EINVAL; return -1;}
int test;
char* ph;
unsigned int wr = 0;
struct iovec iov;
iov.iov_base = &(msg->hdr);
iov.iov_len = sizeof(message_data_hdr_t);
test = writev(fd, &(iov), 1);
if(test == -1){return -1;}
if(msg->hdr.len != 0)
{
ph = msg->buf;
while (wr < msg->hdr.len)
{
test = write(fd, ph, msg->hdr.len - wr);
if(test == -1)
return -1;
else if(test == 0)
{
errno = ENOENT;
return -1;
}
wr += test;
ph += test;
}
}
return test;
}
通过这种方式,我不再发现错误。
感谢您的帮助!