send和recv不一致

时间:2018-04-16 21:07:33

标签: c sockets tcp

好的,所以我们已经连续工作了两天,寻找错误并寻找去年的解决方案和其他软件但是找到了

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

在你抱怨发送和recv缺乏完整性之前,检查不是<我知道我得检查一下。

common.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是一种流协议,没有固定的&#34;包&#34;金额,但我也可以理解如何实现,最多只需要2个字节。我可能会尝试一次只接收1个字节,如果我需要2,那么在循环中这样做,但为什么呢?为什么在单人模式下一切都很好并且混在一起呢?

  2. 我尝试使用recv并相应地发送到其手册页,因此请将它们与套接字一起使用。但是,我不知道为什么,但每当我尝试使用sock_fd的recv时,它只是赢了工作,但conn_fd在其他方面确实如此。但我到处都看到我显然应该使用sock_fd。

  3. 当我混合recv并发送

    时,这是我的输出

    从服务器输出

    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 个答案:

答案 0 :(得分:1)

你有一些未成年人问题和一些大问题:

小:

  • 您不会影响ai_protocol中的hints,但getaddrinfo()不是hints时需要NULL
  • 您使用connect()作为文件描述符返回,但它是错误代码编号。您必须在客户端中使用socket()返回的文件描述符,或在服务器中使用accept()
  • 在服务器中使用send()时,使用了错误的文件描述符。您不应该在处理SOCK_STREAM套接字类型中的连接的文件描述符上发送数据。
  • getaddrinfo()返回一个列表,你应该迭代它,但你假设总有一个结果,但它们可能是零或多于一个。
  • hints已初始化,您使用memset()调用未定义的行为,因为sizeof(struct addrinfo) > sizeof(struct addrinfo *)因此当您在ai上写入零时,您将受到约束(GLOBAL ... GLOBAL EVERYWHERE )

  • 您无缘无故地使用全球
  • 您按行声明多个变量。
  • 您无缘无故地使用全球
  • 您无法验证所有系统调用错误!
  • 您无缘无故地使用全球
// 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
}

答案 1 :(得分:0)

好的,谢谢你们。我非常渴望解决这个问题。感谢所有答案,特别是@Stargateur和@ user3629249。

好的,所以有两件事我需要清除,谢天谢地,我们现在就明白了。

  1. 我需要使用正确的文件描述符,这看起来很明显,但我总是假设使用来自connect / accept或socket的connfd。然而事实证明,user3629249说,我在客户端的连接方法只告诉连接是否已建立,但它不是描述符。所以客户端需要使用socket中的filedescriptor,在我的例子中是sockfd。
  2. 在服务器端,客户端的描述符由accept给出,所以我需要在我的情况下使用connfd。

    1. Beeing迂腐我已经必须使用一些严格的标志,因为我的uni,但是使用更严格的标志是非常有用的。我一直都非常严格地使用Java,但忘记在C中也应用这种心态,这更加重要。
    2. 所以下面我发布我的解决方案来解决这个问题,再次感谢大家!你让我今天一整天都感觉很好!为简单起见,头文件不会更改,但此代码中仍有一些内容。如果您使用类似的东西,请对所有内容进行错误检查,例如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

      ./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

      ./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...