如何在C中的客户端 - 服务器程序中建立双向链接?

问题描述 投票:-2回答:2

我需要在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()函数才能以正确的方式使用文件描述符...所以任何帮助,一些提示或示例将不胜感激。先感谢您。

c network-programming pipe
2个回答
0
投票

好的,我改变了主要功能的主体,如下所示:

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中),程序没有停止。管道看起来仍然是敞开的。我的代码有什么问题?


0
投票

Do not write archaic C

在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.)


Bidirectional communication needs two pipes

为了理智,双向通信需要两个管道。当且仅当,您可以保证孩子发送的数据足够小以适应管道缓冲区(可能小到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
$
© www.soinside.com 2019 - 2024. All rights reserved.