我需要在C中编写两个程序。第一个是带有两个单向链接的客户端 - 服务器程序,第二个是使用双向通信(socet)的同一个,所以它必须是双向的。用户在stdin中键入文件名或文件路径,客户端函数必须读取该文件或文件路径,发送到服务器功能。然后服务器必须读取文件的内容,将其发送回客户端,客户端功能在屏幕上显示该内容(stdout)。我写的第一个程序:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#define MAXBUFF 1024
void err_sys(const char* x){
perror(x);
exit(1);
}
void client(readfd, writefd)
int readfd;
int writefd;
{
char buff[MAXBUFF];
int n;
if(fgets(buff, MAXBUFF, stdin)==NULL)
err_sys("client: filename read error");
n=strlen(buff);
if(buff[n-1]=='\n')
n--;
if(write(writefd, buff, n)!=n)
err_sys("client: filename write error");
while((n=read(readfd, buff, MAXBUFF))>0)
if(write(1, buff, n)!=n)
err_sys("client: data write error");
if(n<0)
err_sys("client: data read error");
}
void server(readfd, writefd)
int readfd;
int writefd;
{
char buff[MAXBUFF];
char errmesg[256];
int n, fd;
if((n=read(readfd, buff, MAXBUFF))<=0)
err_sys("server: filename read error");
buff[n]='\0';
if((fd=open(buff, 0))<0){
if(write(writefd, "error", 5)!=5)
err_sys("server: errmesg write error");
}
else {
while((n=read(fd, buff, MAXBUFF))>0)
if(write(writefd, buff, n)!=n)
err_sys("server: data write error");
if(n<0)
err_sys("server: read error");
}
}
main(){
int childpid, pipe1[2], pipe2[2];
if(pipe(pipe1)<0 || pipe(pipe2)<0)
err_sys("can't create pipes");
if((childpid=fork())<0){
err_sys("can't fork");
}
else if (childpid>0){
close(pipe1[0]);
close(pipe2[1]);
client(pipe2[0], pipe1[1]);
while(wait((int *) 0) != childpid);
close(pipe1[1]);
close(pipe2[0]);
exit(0);
}
else{
close(pipe1[1]);
close(pipe2[0]);
server(pipe1[0], pipe2[1]);
close(pipe1[0]);
close(pipe2[1]);
exit(0);
}
}
但现在我必须修改它只使用一个socet(双向)。我想我必须使用一次pipe()函数,但是有人知道怎么做那个exaclty?我读到某处我必须使用wait()函数才能以正确的方式使用文件描述符...所以任何帮助,一些提示或示例将不胜感激。先感谢您。
好的,我改变了主要功能的主体,如下所示:
main(){
int childpid, pipedes[2];
if(pipe(pipedes)<0)
err_sys("can't create pipes");
if((childpid=fork())<0){
err_sys("can't fork");
}
else if (childpid>0){
client(pipedes[0], pipedes[1]);
close(pipedes[0]);
close(pipedes[1]);
exit(0);
}
else{
wait(childpid);
server(pipedes[0], pipedes[1]);
close(pipedes[1]);
close(pipedes[0]);
exit(0);
}
}
程序读取文件或文件路径的名称,读取文件的内容并将其写入stdout,但之后我仍然可以写一些东西(在stdin中),程序没有停止。管道看起来仍然是敞开的。我的代码有什么问题?
在21世纪,你不应该用K&R风格的函数定义编写ante-diluvian C,也不应该使用main()
的'implicit int'声明。您应该在C11(或者至少是C99)模式下进行编译,在这种情况下不允许这样的事情。 (严格来说,C11仍然允许K&R风格的功能,但你永远不应该写它们。原型远远优于K&R风格。)
因此,像这样的函数定义是非常糟糕的代码 - 我书中的即时失败等级。您可能需要识别符号,但是您永远不应该写它。
void client(readfd, writefd) // Wrong!
int readfd; // Archaic!
int writefd; // Do not use!
{
你应该使用:
void client(int readfd, int writefd)
{
同理:
main() {
太可怕了你应该明确指定返回类型,你应该真正指定它不需要与void
的参数,如:
int main(void) {
(int main()
和int main(void)
之间的选择并不像没有省略int
那样明确 - 如果你看一下标准C,你会发现几个int main()
和几个int main(void)
的例子。但是,你会发现没有例子没有int
;在整个当前的千禧年里,它不是有效的标准C.)
为了理智,双向通信需要两个管道。当且仅当,您可以保证孩子发送的数据足够小以适应管道缓冲区(可能小到4 KiB;历史上,它通常是5 KiB,但在许多现代系统上它是64 KiB),那么你可以使用一个管道,但没有这个保证,你不能轻易地在两个进程之间进行协调。只要您想在每个方向发送多条消息,就需要两个管道(并且您需要一个协议,以便编写者可以在读取当前消息的结尾时让读者知道)。
这里有一些有用的代码。它基于您问题中的代码,而不是“答案”中的代码,而不是答案。它是在文件pipe13.c
并编译为pipe13
:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAXBUFF 1024
static
void err_sys(const char *msg)
{
perror(msg);
exit(1);
}
static
void client(int readfd, int writefd)
{
char buff[MAXBUFF];
if (fgets(buff, MAXBUFF, stdin) == NULL)
err_sys("client: filename read error");
int n = strlen(buff);
if (buff[n - 1] == '\n')
buff[--n] = '\0';
/* Write the null byte too */
if (write(writefd, buff, n+1) != n+1)
err_sys("client: filename write error");
close(writefd);
while ((n = read(readfd, buff, MAXBUFF)) > 0)
{
static const char marker[] = "\n<client>:\n";
enum { MARKER_SZ = sizeof(marker) - 1 };
if (write(STDOUT_FILENO, marker, MARKER_SZ) != MARKER_SZ)
err_sys("client: marker write error");
if (write(STDOUT_FILENO, buff, n) != n)
err_sys("client: data write error");
}
static const char end[] = "\n</client>\n";
enum { END_SZ = sizeof(end) - 1 };
if (write(STDOUT_FILENO, end, END_SZ) != END_SZ)
err_sys("client: end marker write error");
close(readfd);
if (n < 0)
err_sys("client: data read error");
}
static
void server(int readfd, int writefd)
{
char buff[MAXBUFF];
int n, fd;
if ((n = read(readfd, buff, MAXBUFF)) <= 0)
err_sys("server: filename read error");
printf("server: filename [%s]\n", buff);
close(readfd);
if ((fd = open(buff, O_RDONLY)) < 0)
{
if (write(writefd, "error", 5) != 5)
err_sys("server: errmesg write error");
}
else
{
while ((n = read(fd, buff, MAXBUFF)) > 0)
{
static const char marker[] = "\n<server>:\n";
enum { MARKER_SZ = sizeof(marker) - 1 };
if (write(writefd, marker, MARKER_SZ) != MARKER_SZ)
err_sys("server: marker write error");
if (write(writefd, buff, n) != n)
err_sys("server: data write error");
}
static const char end[] = "\n</server>\n";
enum { END_SZ = sizeof(end) - 1 };
if (write(writefd, end, END_SZ) != END_SZ)
err_sys("server: end marker write error");
close(writefd);
if (n < 0)
err_sys("server: read error");
}
}
int main(void)
{
int childpid, pipe1[2], pipe2[2];
if (pipe(pipe1) < 0 || pipe(pipe2) < 0)
err_sys("can't create pipes");
if ((childpid = fork()) < 0)
err_sys("can't fork");
else if (childpid > 0)
{
close(pipe1[0]);
close(pipe2[1]);
client(pipe2[0], pipe1[1]);
}
else
{
close(pipe1[1]);
close(pipe2[0]);
server(pipe1[0], pipe2[1]);
}
int corpse;
int status;
while ((corpse = wait(&status)) != -1)
{
printf("%d: child PID %d exited with status 0x%.4X\n",
(int)getpid(), corpse, status);
}
return 0;
}
当它编译并运行在自己的源代码上时(使用Bash):
$ make pipe13 && ./pipe13 <<< pipe13.c
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes pipe13.c -o pipe13
server: filename [pipe13.c]
<client>:
<server>:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
…
static const char end[]
<client>:
= "\n</clie
<server>:
nt>\n";
enum { END_SZ = sizeof(end) - 1 };
if (write(STDOUT_FILENO, end, END_SZ) != END_SZ)
err_sys("client: end marker write error");
…
if (write(writefd, buff, n) != n)
err_sys
<client>:
("server: data write e
<server>:
rror");
}
static const char end[] = "\n</server>\n";
…
return 0;
}
</server>
</client>
28996: child PID 28997 exited with status 0x0000
$