代码之家  ›  专栏  ›  技术社区  ›  Levent Dag

发送和接收不一致

  •  0
  • Levent Dag  · 技术社区  · 7 年前

    好了,伙计们,我已经连续工作了两天,搜索bug,寻找去年的解决方案和其他软件,但发现

    我的问题是,我需要编写一个相当简单的服务器客户机应用程序,在这个应用程序中,两者都接受响应并回复它们。我写了一些小程序来演示我的问题。

    在您抱怨要检查的发送和接收不完整之前,请不要<我知道我得检查一下。

    常见的h只是所有输入和静态变量的标题。

    // guard block:
    #ifndef COMMON_H
    #define COMMON_H
    
    // default hostname and port:
    #define DEFAULT_HOST    "localhost"
    #define DEFAULT_PORT    "1280"
    
    #include <stdarg.h>
    #include <ctype.h> 
    
    // IO, C standard library, POSIX API, data types:
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdint.h>
    #include <stdbool.h>
    #include <string.h>
    #include <limits.h>
    
    // Sockets, TCP, ... :
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <fcntl.h>
    
    // Assertions, errors, signals:
    #include <assert.h>
    #include <errno.h>
    #include <signal.h>
    
    static struct addrinfo *ai = NULL;      // stores address information
    static int sockfd = -1;                 // socket file descriptor
    static int connfd = -1;                 // connection file descriptor
    
    void terminate(int sig);
    void init_signal_handler(void);
    void free_ressources(void);
    void error(char* format, ...);
    
    void terminate(int sig) {
      error("Caught signal %d. Terminating...\n",sig);
    }
    
    void init_signal_handler(void) {
      struct sigaction sa;
      sa.sa_handler = terminate;
      if (-1 == sigemptyset(&(sa.sa_mask))) {
        error("sigemptyset()");
      }
      if (-1 == sigaction(SIGINT, &sa, NULL)) {
        error("sigaction()");
      }
      if (-1 == sigaction(SIGTERM, &sa, NULL)) {
        error("sigaction()");
      }
    }
    
    void error(char *format, ...) {
      va_list arg;
    
      va_start (arg, format);
      (void) vfprintf (stderr, format, arg);
      va_end (arg);
    
      free_ressources();
    
      exit(EXIT_FAILURE);
    }
    
    void free_ressources(void) {
      (void) printf("Freeing all ressources...\n");
      if(sockfd != -1) close(sockfd);
      if(connfd != -1) close(connfd);
      freeaddrinfo(ai);
    }
    #endif // COMMON_H
    

    这就是客户

    #include "common.h"
    
    static char *port = DEFAULT_PORT;
    static char *host = DEFAULT_HOST;
    
    int main(int argc, char *argv[]) {
      int s;
      struct addrinfo hints, *rp;
      memset(&ai, 0, sizeof(hints));
      hints.ai_family = AF_INET;
      hints.ai_socktype = SOCK_STREAM;
      hints.ai_flags = AI_PASSIVE;
    
      s = getaddrinfo(host, port, &hints, &ai);
      if(s != 0) error("Couldn't get address info...\n");
    
      for (rp=ai; rp != NULL; rp=rp->ai_next) {
          sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
          if(sockfd == -1) continue;
          if((connfd = connect(sockfd, rp->ai_addr, rp->ai_addrlen)) != -1) break;
          close(sockfd);
      }
    
      if(rp == NULL) error("Couldn't connect...\n");
    
      uint16_t msg = 0xF0F0;
      uint8_t msg_r = 0x00;
      for(int i = 0; i < 10; ++i) {
        (void) fprintf(stdout, "Sending message %#X\n", msg);
        send(sockfd, &msg, 2, MSG_CONFIRM);
        recv(connfd, &msg_r, 1, MSG_CONFIRM);
        (void) fprintf(stdout, "Recieved from Server %#X\n", msg_r);
        msg+=1;
      }
      free_ressources();
    }
    

    这是服务器

    #include "common.h"
    
    static char *port = DEFAULT_PORT;
    
    int main(int argc, char *argv[]) {
      struct addrinfo hints;
      memset(&ai, 0, sizeof(hints));
      hints.ai_family = AF_INET;
      hints.ai_socktype = SOCK_STREAM;
      hints.ai_flags = AI_PASSIVE;
    
      int res = getaddrinfo(NULL, port, &hints, &ai);
      if(res != 0) error("Failed to get addr info: %s\n", gai_strerror(res));
    
      sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
      if(sockfd == -1) error("Couldn't create a socket\n");
    
      int val = 1;
      res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val);
      if(res == -1) error("Socket options couldn't be set\n");
    
      res = bind(sockfd, ai->ai_addr, ai->ai_addrlen);
      if(res == -1) error("Socket binding failed\n");
    
      res = listen(sockfd, 1);
      if(res == -1) error("Listener setup failed\n");
    
      connfd = accept(sockfd, NULL, NULL);
      if(connfd == -1) error("Connect failed\n");
    
      uint8_t msg_s = 0x00;
      uint16_t msg = 0x0000;
      for(int i = 0; i < 10; ++i) {
        msg_s+=1;
        recv(connfd, &msg, 2, MSG_CONFIRM);   
        (void) fprintf(stdout, "Recieved from Client %#X\n", msg);
        (void) fprintf(stdout, "Sending %#X\n", msg_s);
        send(sockfd, &msg_s, 1, MSG_CONFIRM);
      }
      free_ressources();
    }
    

    好的,我有几个问题。

    1. 如果我在一个方向上执行服务器-客户机应用程序,就像只有客户机发送和只有服务器接收一样,一切都会像往常一样顺利。然而,当我把它像这里一样混在一起时,事情就变了。我知道TCP是一种没有固定“包”数量的流协议,但我也知道如何实现它,最多只需要2个字节。如果需要2个字节,我可能会尝试一次只接收1个字节,然后在循环中这样做,但为什么呢?为什么在单行道模式下一切都很好,而在混合模式下却见鬼去了?

    2. 我尝试使用recv并相应地发送到它的手册页,所以将它们与套接字一起使用。然而,我不知道为什么,但每当我尝试将recv与sock\u fd一起使用时,它都不会起作用,但另一端的conn\u fd会起作用。但我到处都看到我应该使用sock\u fd。

    这是我混合接收和发送时的输出

    来自服务器的输出

    Recieved from Client 0XF0F0
    Sending 0X1
    

    来自客户端的输出

    Sending message 0XF0F0
    Recieved from Server 0
    Sending message 0XF0F1
    Recieved from Server 0
    Sending message 0XF0F2
    Recieved from Server 0
    Sending message 0XF0F3
    Recieved from Server 0
    Sending message 0XF0F4
    Recieved from Server 0
    Sending message 0XF0F5
    Recieved from Server 0
    Sending message 0XF0F6
    Recieved from Server 0
    Sending message 0XF0F7
    Recieved from Server 0
    Sending message 0XF0F8
    Recieved from Server 0
    Sending message 0XF0F9
    Recieved from Server 0
    Freeing all ressources...
    

    总之,我知道这可能是一个愚蠢而简单的问题,但为什么会是这样呢?为什么服务器在发送第一条消息时会崩溃,而客户端则会将其消息扔到空处。

    谢谢你提前回复,我真的需要解决这个问题。

    2 回复  |  直到 7 年前
        1
  •  1
  •   Stargateur    7 年前

    你有一些小问题和一些大问题:

    小型:

    • 你不影响 ai_protocol 在里面 hints ,但它是 getaddrinfo() 什么时候 提示 不是 NULL
    • 您使用 connect() 作为文件描述符,但它是一个错误代码。您必须使用返回的文件描述符 socket() 在客户端中,或通过 accept() 在服务器中。
    • 使用时使用了错误的文件描述符 send() 在服务器中。您不应该在处理连接的文件描述符上发送数据 SOCK_STREAM 插座类型。
    • getaddrinfo() 返回一个列表,您应该对其进行迭代,但您假设总是有一个结果,但它们可以是零或多个。
    • 提示 初始化时,调用未定义的行为 memset() 因为 sizeof(struct addrinfo) > sizeof(struct addrinfo *) 所以当你在上面写零的时候,你就越界了 ai (全球……全球各地)

    大型:

    • 您无理由使用global
    • 您可以按行声明多个变量。
    • 您无理由使用global
    • 您没有验证所有系统调用错误!
    • 您无理由使用global

    // server and client
    struct addrinfo hints = {
      .ai_family = AF_INET,
      .ai_socktype = SOCK_STREAM,
      .ai_flags = AI_PASSIVE,
      .ai_protocol = IPPROTO_TCP,
    };
    // server
    if (send(connfd, &msg_s, 1, MSG_CONFIRM) == -1) {
      // error
    }
    // client
    if (recv(sockfd, &msg_r, 1, MSG_CONFIRM) == -1) {
      // error
    }
    
        2
  •  0
  •   Levent Dag    7 年前

    好的,哇,谢谢大家。我非常渴望解决这个问题。感谢所有的回答,尤其是@Stargateur和@user3629249。

    好吧,有两件事我需要弄清楚,感谢你们,我现在明白了。

    1. 我需要使用正确的文件描述符,这似乎很明显,但我总是假设使用连接/接受的connfd或套接字。然而,正如user3629249所说,客户端的connect方法只告诉是否建立了连接,而不是描述符。因此,客户端需要使用套接字中的filedescriptor,在我的示例中是sockfd。

    在服务器端,客户端的描述符由accept给定,因此我需要在我的示例中使用connfd。

    1. 由于我的uni,我已经必须使用一些严格的标志,但是使用更严格的标志真的很有用。我一直对Java非常严格,但忘记了在C语言中也应用这种思想,因为C语言更重要。

    所以我在下面发布了我对这个问题的解决方案,再次感谢大家!你让我开心!为了简单起见,头文件没有更改,但是这段代码中还剩下一些东西。如果您使用类似的方法,请检查所有内容的错误,如recv和send方法,并请阅读Stargateur和user3629249的评论,这将帮助您理解我的代码失败的原因,现在可以工作了。

    生成文件

    CC      = /usr/bin/gcc
    CFLAGS  = -std=c99 -Wall -Wextra -Wconversion -pedantic -std=gnu11 -D_XOPEN_SOURCE=500 -D_BSD_SOURCE -g -c
    SERVER = server
    CLIENT = client
    
    all: $(SERVER).c $(CLIENT).c clean compile compile2
    
    compile: $(SERVER).c $(CLIENT).c 
      gcc $(CFLAGS) $(SERVER).c
      gcc $(CFLAGS) $(CLIENT).c
    
    compile2: $(SERVER).o $(CLIENT).o
      gcc $(SERVER).o -o $(SERVER)
      gcc $(CLIENT).o -o $(CLIENT)
    
    clean: $(SERVER) $(SERVER).o $(CLIENT) $(CLIENT).o
      rm $(SERVER) $(SERVER).o
      rm $(CLIENT) $(CLIENT).o
    

    客户

    #include "common.h"
    
    static char *port = DEFAULT_PORT;
    static char *host = DEFAULT_HOST;
    
    int main() {
      int s;
      struct addrinfo hints, *rp;
      memset(&ai, 0, sizeof(hints));
      hints.ai_family = AF_INET;
      hints.ai_socktype = SOCK_STREAM;
      hints.ai_flags = AI_PASSIVE;
    
      s = getaddrinfo(host, port, &hints, &ai);
      if(s != 0) error("Couldn't get address info...\n");
    
      for (rp=ai; rp != NULL; rp=rp->ai_next) {
        sockfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if(sockfd == -1) continue;
        if((connfd = connect(sockfd, rp->ai_addr, rp->ai_addrlen)) != -1) break;
        close(sockfd);
      }
    
      if(rp == NULL) error("Couldn't connect...\n");
    
      uint8_t msg = 0x00;
      for(int i = 0; i < 10; ++i) {
        msg=(uint8_t)((int)msg+1);
        (void) fprintf(stdout, "Sending message %#X\n", msg);
        send(sockfd, &msg, 1, MSG_CONFIRM);
        recv(sockfd, &msg, 1, MSG_CONFIRM);
        (void) fprintf(stdout, "Recieved from Server %#X\n", msg);
      }
      free_ressources();
    }
    

    服务器

    #include "common.h"
    
    static char *port = DEFAULT_PORT;
    
    uint16_t read_from_client();
    
    int main() {
      struct addrinfo hints;
      memset(&ai, 0, sizeof(hints));
      hints.ai_family = AF_INET;
      hints.ai_socktype = SOCK_STREAM;
      hints.ai_flags = AI_PASSIVE;
      hints.ai_protocol = 0;
    
      int res = getaddrinfo(NULL, port, &hints, &ai);
      if(res != 0) error("Failed to get addr info: %s\n", gai_strerror(res));
    
      sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
      if(sockfd == -1) error("Couldn't create a socket\n");
    
      int val = 1;
      res = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val);
      if(res == -1) error("Socket options couldn't be set\n");
    
      res = bind(sockfd, ai->ai_addr, ai->ai_addrlen);
      if(res == -1) error("Socket binding failed\n");
    
      res = listen(sockfd, 1);
      if(res == -1) error("Listener setup failed\n");
    
      connfd = accept(sockfd, NULL, NULL);
      if(connfd == -1) error("Server Accept failed\n");
    
      uint8_t msg = 0x00;
      for(int i = 0; i < 10; ++i) {
        recv(connfd, &msg, 1, MSG_CONFIRM);   
        (void) fprintf(stdout, "Recieved from Client %#X\n", msg);
        msg = (uint8_t) ((int)msg + 1);
        (void) fprintf(stdout, "Sending %#X\n", msg);
        send(connfd, &msg, 1, MSG_CONFIRM);
      }
      free_ressources();
    }
    

    因此,输出符合预期。来自/服务器

    ./server 
    Recieved from Client 0X1
    Sending 0X2
    Recieved from Client 0X3
    Sending 0X4
    Recieved from Client 0X5
    Sending 0X6
    Recieved from Client 0X7
    Sending 0X8
    Recieved from Client 0X9
    Sending 0XA
    Recieved from Client 0XB
    Sending 0XC
    Recieved from Client 0XD
    Sending 0XE
    Recieved from Client 0XF
    Sending 0X10
    Recieved from Client 0X11
    Sending 0X12
    Recieved from Client 0X13
    Sending 0X14
    Freeing all ressources...
    

    来自/客户

    ./client
    Sending message 0X1
    Recieved from Server 0X2
    Sending message 0X3
    Recieved from Server 0X4
    Sending message 0X5
    Recieved from Server 0X6
    Sending message 0X7
    Recieved from Server 0X8
    Sending message 0X9
    Recieved from Server 0XA
    Sending message 0XB
    Recieved from Server 0XC
    Sending message 0XD
    Recieved from Server 0XE
    Sending message 0XF
    Recieved from Server 0X10
    Sending message 0X11
    Recieved from Server 0X12
    Sending message 0X13
    Recieved from Server 0X14
    Freeing all ressources...