read() / write() 如何与 FIFO 配合使用? - client/server 程序中的问题
How read() / write() works with FIFO? - Issue in a client/server program
我有一个 client/server 程序。
客户端循环执行以下操作。
- 在管道上写入 string1。
- 从另一个管道读取字符串。
- 在管道上写入 string2。
服务器循环执行以下操作。
- 读取字符串。
- 将相同的数据写入其他管道上的客户端。
对于某些迭代,这工作正常,在大约 10 次迭代后,在客户端和服务器上只能看到 string2 被读取。
为什么会这样?
此外,如果在第二次 write() 之后调用 read(),则在 while() 循环中的客户端程序中一切正常。
在客户端中,从第二次迭代开始,read() 应该 return 管道中的全部数据,因为管道没有消息边界。但它只读取服务器中 1 次 write() 调用写入的数据。
下面是我的代码供参考。
server.c
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
int main()
{
client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
char buf[BUFSIZ];
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
mkfifo(myfifo2, 0666);
/* open, read, and display the message from the FIFO */
client_to_server = open(myfifo, O_RDONLY);
server_to_client = open(myfifo2, O_WRONLY);
printf("Server ON bufsize=%d.\n", BUFSIZ);
while (1)
{
read(client_to_server, buf, BUFSIZ);
if (strcmp("exit",buf)==0)
{
printf("Server OFF.\n");
break;
}
else if (strcmp("",buf)!=0)
{
printf("Received: %s\n", buf);
printf("Sending back...\n");
write(server_to_client,buf,BUFSIZ);
}
/* clean buf from any data */
memset(buf, 0, sizeof(buf));
}
close(client_to_server);
close(server_to_client);
unlink(myfifo);
unlink(myfifo2);
return 0;
}
client.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include<string.h>
int main()
{
int client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
char str[BUFSIZ];
/* write str to the FIFO */
client_to_server = open(myfifo, O_WRONLY);
server_to_client = open(myfifo2, O_RDONLY);
char buf1[30] = "str1";
char buf2[30] = "str2";
while(1){
//write first string
write(client_to_server, buf1, sizeof(buf1));
read(server_to_client,str,sizeof(str));
perror("Read:"); // Very crude error check
printf("...received from the server: %s\n",str);
memset(str, '[=12=]', sizeof(str));
//write second string
write(client_to_server, buf2, sizeof(buf2));
}
close(client_to_server);
close(server_to_client);
return 0;
}
下面是输出:
服务器输出:
服务器开启 bufsize=8192。
收到:str1
发回...
收到:str2
发回...
收到:str1
发回...
收到:str2
发回...
收到:str1
发回...
收到:str2
发回...
收到:str1
发回...
收到:str2
发回...
收到:str1
发回...
收到:str2
发回...
收到:str1
发回...
收到:str2
发回...
收到:str1
发回...
收到:str2
发回...
收到:str1
发回...
收到:str2
发回...
收到:str1
发回...
收到:str2
发回...
收到:str1
发回...
收到:str2
发回...
收到:str2
发回...
收到:str2
发回...
收到:str2
发回...
收到:str2
发回...
收到:str2
发回...
收到:str2
发回...
收到:str2
以下是客户端的输出:
...从服务器收到:str1
...从服务器收到:str2
...从服务器收到:str1
...从服务器收到:str2
...从服务器收到:str1
...从服务器收到:str2
...从服务器收到:str1
...从服务器收到:str2
...从服务器收到:str1
...从服务器收到:str2
...从服务器收到:str1
...从服务器收到:str2
...从服务器收到:str1
...从服务器收到:str2
...从服务器收到:str1
...从服务器收到:str2
...从服务器收到:str1
...从服务器收到:str2
...从服务器收到:str1
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
...从服务器收到:str2
简短的回答是客户端和服务器接收 "str2[=20=]str1[=20=]" 但只打印到第一个 \0,即 "str2".
长的答案是在客户端,你发送的不仅仅是字符串 "str1" 和 "str2",还有终止 NUL:
write(client_to_server, buf1, sizeof(buf1));
这里的sizeofreturns字符串的长度加上终止NUL。
现在是服务器端。您最多读取 BUFSIZ 字节。因此,如果客户端足够快以发送少量字符串,即 "str2[=20=]str1[=20=]" 您将一次性读取所有字符串。要解决此问题,您必须考虑读取的内容 returns,即:
ssize_t ret = read(client_to_server, buf, BUFSIZ);
printf("Received: size %zd\n", ret); // print the size
fwrite(buf, 1, ret, stdout); // print the buffer with NULs
write(server_to_client,buf,ret); // write back just the data you actually received
您不检查错误,也不使用 read(2)
中的 return 值,它表示读取了 return 的字符数。这很奇怪,因为这是不同步的服务器和客户端的来源。我已经以不会失败的方式重写了您的代码(尽管我没有检查 write(2)
的 return 值,这应该会在破坏管道时失败 --- 在杀死服务器或客户端时)这是留给你完成的,作为练习。
关注 printf(3)
调用如何处理非空终止字符串,通过指定 "%.*s"
格式的字符串长度(这是避免将最终 [= 16=]
在缓冲区中(如果您正在阅读 sizeof buf
,而不是 sizeof buf - 1
,应该检查它。
server.c
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define F(fmt) __FILE__":%d:%s: " fmt, __LINE__, __func__
/* file scope to share with at_exit() call */
static const char myfifo[] = "/tmp/client_to_server_fifo";
static const char myfifo2[] = "/tmp/server_to_client_fifo";
void rm_fifos(void)
{
printf(F("deleting fifo \"%s\"\n"), myfifo);
unlink(myfifo);
printf(F("deleting fifo2 \"%s\"\n"), myfifo2);
unlink(myfifo2);
}
int main()
{
int client_to_server;
int server_to_client;
int res;
char buf[BUFSIZ];
/* create the FIFO (named pipe) */
printf(F("creating fifo \"%s\"\n"), myfifo);
res = mkfifo(myfifo, 0666 | O_EXCL); /* O_EXCL guarantees that no second instance is created while this is running */
if (res < 0) {
fprintf(stderr,
F("%s: %s(errno=%d)\n"),
myfifo, strerror(errno), errno);
exit(EXIT_FAILURE);
}
printf(F("creating fifo \"%s\"\n"), myfifo);
res = mkfifo(myfifo2, 0666 | O_EXCL);
if (res < 0) {
fprintf(stderr,
F("%s: %s(errno=%d)\n"),
myfifo2, strerror(errno), errno);
unlink(myfifo); /* we successfuly created it */
exit(EXIT_FAILURE);
}
atexit(rm_fifos);
/* open, read, and display the message from the FIFO */
client_to_server = open(myfifo, O_RDONLY);
if (client_to_server < 0) {
fprintf(stderr,
F("%s: open: %s(errno=%d)\n"),
myfifo, strerror(errno), errno);
exit(EXIT_FAILURE);
}
server_to_client = open(myfifo2, O_WRONLY);
if (server_to_client < 0) {
fprintf(stderr,
F("%s: open: %s(errno=%d)\n"),
myfifo2, strerror(errno), errno);
exit(EXIT_FAILURE);
}
printf(F("Server ON bufsize=%d.\n"), BUFSIZ);
while (1) {
ssize_t n = read(client_to_server, buf, BUFSIZ);
if (n < 0) {
fprintf(stderr,
F("%s: read: %s(errno = %d)\n"),
myfifo, strerror(errno), errno);
exit(EXIT_FAILURE);
}
if (n == 0) break; /* EOF on input */
printf("Received: [%.*s]\n", n, buf);
printf("Sending back...\n");
write(server_to_client, buf, n); /* very important to write just the n read chars, and not more */
/* no need to clean buf from any data */
}
close(client_to_server);
close(server_to_client);
/* no need to unlink, as this was prepared in the atexit() library call */
return 0;
}
client.c
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define F(fmt) __FILE__ ":%d:%s: " fmt, __LINE__, __func__
int main()
{
int client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
char str[BUFSIZ];
/* write str to the FIFO */
client_to_server = open(myfifo, O_WRONLY);
if (client_to_server < 0) {
fprintf(stderr,
F("%s: %s(errno = %d)\n"),
myfifo, strerror(errno), errno);
exit(EXIT_FAILURE);
}
server_to_client = open(myfifo2, O_RDONLY);
if (server_to_client < 0) {
fprintf(stderr,
F("%s: %s(errno = %d)\n"),
myfifo2, strerror(errno), errno);
exit(EXIT_FAILURE);
}
char buf1[30] = "str1";
char buf1_length = strlen(buf1);
char buf2[30] = "str2";
char buf2_length = strlen(buf2);
while(1) {
ssize_t n;
//write first string
write(client_to_server, buf1, buf1_length);
n = read(server_to_client, str, sizeof(str));
if (n < 0) {
fprintf(stderr,
F("%s: %s(errno = %d)\n"),
myfifo2, strerror(errno), errno);
exit(EXIT_FAILURE);
}
if (n == 0) break;
printf("...received from the server: %.*s\n", n, str);
/* NO NEED TO CLEAN THE BUFFER */
//write second string
write(client_to_server, buf2, buf2_length);
}
close(client_to_server);
close(server_to_client);
return 0;
}
我有一个 client/server 程序。
客户端循环执行以下操作。
- 在管道上写入 string1。
- 从另一个管道读取字符串。
- 在管道上写入 string2。
服务器循环执行以下操作。
- 读取字符串。
- 将相同的数据写入其他管道上的客户端。
对于某些迭代,这工作正常,在大约 10 次迭代后,在客户端和服务器上只能看到 string2 被读取。 为什么会这样? 此外,如果在第二次 write() 之后调用 read(),则在 while() 循环中的客户端程序中一切正常。
在客户端中,从第二次迭代开始,read() 应该 return 管道中的全部数据,因为管道没有消息边界。但它只读取服务器中 1 次 write() 调用写入的数据。
下面是我的代码供参考。
server.c
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
int main()
{
client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
char buf[BUFSIZ];
/* create the FIFO (named pipe) */
mkfifo(myfifo, 0666);
mkfifo(myfifo2, 0666);
/* open, read, and display the message from the FIFO */
client_to_server = open(myfifo, O_RDONLY);
server_to_client = open(myfifo2, O_WRONLY);
printf("Server ON bufsize=%d.\n", BUFSIZ);
while (1)
{
read(client_to_server, buf, BUFSIZ);
if (strcmp("exit",buf)==0)
{
printf("Server OFF.\n");
break;
}
else if (strcmp("",buf)!=0)
{
printf("Received: %s\n", buf);
printf("Sending back...\n");
write(server_to_client,buf,BUFSIZ);
}
/* clean buf from any data */
memset(buf, 0, sizeof(buf));
}
close(client_to_server);
close(server_to_client);
unlink(myfifo);
unlink(myfifo2);
return 0;
}
client.c
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include<string.h>
int main()
{
int client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
char str[BUFSIZ];
/* write str to the FIFO */
client_to_server = open(myfifo, O_WRONLY);
server_to_client = open(myfifo2, O_RDONLY);
char buf1[30] = "str1";
char buf2[30] = "str2";
while(1){
//write first string
write(client_to_server, buf1, sizeof(buf1));
read(server_to_client,str,sizeof(str));
perror("Read:"); // Very crude error check
printf("...received from the server: %s\n",str);
memset(str, '[=12=]', sizeof(str));
//write second string
write(client_to_server, buf2, sizeof(buf2));
}
close(client_to_server);
close(server_to_client);
return 0;
}
下面是输出: 服务器输出: 服务器开启 bufsize=8192。 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str1 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2 发回... 收到:str2
以下是客户端的输出: ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str1 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2 ...从服务器收到:str2
简短的回答是客户端和服务器接收 "str2[=20=]str1[=20=]" 但只打印到第一个 \0,即 "str2".
长的答案是在客户端,你发送的不仅仅是字符串 "str1" 和 "str2",还有终止 NUL:
write(client_to_server, buf1, sizeof(buf1));
这里的sizeofreturns字符串的长度加上终止NUL。
现在是服务器端。您最多读取 BUFSIZ 字节。因此,如果客户端足够快以发送少量字符串,即 "str2[=20=]str1[=20=]" 您将一次性读取所有字符串。要解决此问题,您必须考虑读取的内容 returns,即:
ssize_t ret = read(client_to_server, buf, BUFSIZ);
printf("Received: size %zd\n", ret); // print the size
fwrite(buf, 1, ret, stdout); // print the buffer with NULs
write(server_to_client,buf,ret); // write back just the data you actually received
您不检查错误,也不使用 read(2)
中的 return 值,它表示读取了 return 的字符数。这很奇怪,因为这是不同步的服务器和客户端的来源。我已经以不会失败的方式重写了您的代码(尽管我没有检查 write(2)
的 return 值,这应该会在破坏管道时失败 --- 在杀死服务器或客户端时)这是留给你完成的,作为练习。
关注 printf(3)
调用如何处理非空终止字符串,通过指定 "%.*s"
格式的字符串长度(这是避免将最终 [= 16=]
在缓冲区中(如果您正在阅读 sizeof buf
,而不是 sizeof buf - 1
,应该检查它。
server.c
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define F(fmt) __FILE__":%d:%s: " fmt, __LINE__, __func__
/* file scope to share with at_exit() call */
static const char myfifo[] = "/tmp/client_to_server_fifo";
static const char myfifo2[] = "/tmp/server_to_client_fifo";
void rm_fifos(void)
{
printf(F("deleting fifo \"%s\"\n"), myfifo);
unlink(myfifo);
printf(F("deleting fifo2 \"%s\"\n"), myfifo2);
unlink(myfifo2);
}
int main()
{
int client_to_server;
int server_to_client;
int res;
char buf[BUFSIZ];
/* create the FIFO (named pipe) */
printf(F("creating fifo \"%s\"\n"), myfifo);
res = mkfifo(myfifo, 0666 | O_EXCL); /* O_EXCL guarantees that no second instance is created while this is running */
if (res < 0) {
fprintf(stderr,
F("%s: %s(errno=%d)\n"),
myfifo, strerror(errno), errno);
exit(EXIT_FAILURE);
}
printf(F("creating fifo \"%s\"\n"), myfifo);
res = mkfifo(myfifo2, 0666 | O_EXCL);
if (res < 0) {
fprintf(stderr,
F("%s: %s(errno=%d)\n"),
myfifo2, strerror(errno), errno);
unlink(myfifo); /* we successfuly created it */
exit(EXIT_FAILURE);
}
atexit(rm_fifos);
/* open, read, and display the message from the FIFO */
client_to_server = open(myfifo, O_RDONLY);
if (client_to_server < 0) {
fprintf(stderr,
F("%s: open: %s(errno=%d)\n"),
myfifo, strerror(errno), errno);
exit(EXIT_FAILURE);
}
server_to_client = open(myfifo2, O_WRONLY);
if (server_to_client < 0) {
fprintf(stderr,
F("%s: open: %s(errno=%d)\n"),
myfifo2, strerror(errno), errno);
exit(EXIT_FAILURE);
}
printf(F("Server ON bufsize=%d.\n"), BUFSIZ);
while (1) {
ssize_t n = read(client_to_server, buf, BUFSIZ);
if (n < 0) {
fprintf(stderr,
F("%s: read: %s(errno = %d)\n"),
myfifo, strerror(errno), errno);
exit(EXIT_FAILURE);
}
if (n == 0) break; /* EOF on input */
printf("Received: [%.*s]\n", n, buf);
printf("Sending back...\n");
write(server_to_client, buf, n); /* very important to write just the n read chars, and not more */
/* no need to clean buf from any data */
}
close(client_to_server);
close(server_to_client);
/* no need to unlink, as this was prepared in the atexit() library call */
return 0;
}
client.c
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define F(fmt) __FILE__ ":%d:%s: " fmt, __LINE__, __func__
int main()
{
int client_to_server;
char *myfifo = "/tmp/client_to_server_fifo";
int server_to_client;
char *myfifo2 = "/tmp/server_to_client_fifo";
char str[BUFSIZ];
/* write str to the FIFO */
client_to_server = open(myfifo, O_WRONLY);
if (client_to_server < 0) {
fprintf(stderr,
F("%s: %s(errno = %d)\n"),
myfifo, strerror(errno), errno);
exit(EXIT_FAILURE);
}
server_to_client = open(myfifo2, O_RDONLY);
if (server_to_client < 0) {
fprintf(stderr,
F("%s: %s(errno = %d)\n"),
myfifo2, strerror(errno), errno);
exit(EXIT_FAILURE);
}
char buf1[30] = "str1";
char buf1_length = strlen(buf1);
char buf2[30] = "str2";
char buf2_length = strlen(buf2);
while(1) {
ssize_t n;
//write first string
write(client_to_server, buf1, buf1_length);
n = read(server_to_client, str, sizeof(str));
if (n < 0) {
fprintf(stderr,
F("%s: %s(errno = %d)\n"),
myfifo2, strerror(errno), errno);
exit(EXIT_FAILURE);
}
if (n == 0) break;
printf("...received from the server: %.*s\n", n, str);
/* NO NEED TO CLEAN THE BUFFER */
//write second string
write(client_to_server, buf2, buf2_length);
}
close(client_to_server);
close(server_to_client);
return 0;
}