Levent Dag
Levent Dag

Reputation: 170

send and recv are not consistent

Ok so guys i've been working like 2 days straight, searching for bugs and looking for last years solutions and other software but found

My problem is that i need to program a fairly easy server client application in which both take responses and reply to them. I wrote smaller programs to just demonstrate my problem.

And before you complain about the lack of completeness with send and recv to be checked not be < 0. I know that i gotta check that.

common.h is just a header for all the inputs and static variables.

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

So here is the client

#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();
}

And here is the server

#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();
}

Ok so i have several problems with this.

  1. If i do a server client application in one direction, like only client sends and only server recieves, everything goes smooth and expected as always. However things change when i mix it up like here. I understand that TCP is a stream protocol without fixed "package" amounts, but i also can understand how to implement that with just needing 2 bytes max. I might try to just recieve 1 byte at a time and do that in a loop if i need 2, but why though? Why is everything fine at onelane mode and goes hell in mixed?

  2. I tried to use recv and send accordingly to its man pages so use them with the socket. However i don't know why, but whenever i try to use recv with the sock_fd it just won't work, but conn_fd on the otherside does. But i see everywhere that i clearly should use sock_fd.

This is my output when i mix up the recv and send

OUTPUT FROM SERVER

Recieved from Client 0XF0F0
Sending 0X1

OUTPUT FROM CLIENT

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

So in conclusion, i know maybe it's a stupid and simple question, but why is it like that? Why does the server crash at sending the very first message, whereas the client seams to throw out his messages into the void.

Thanks for any answers in advance, i really need to solve this.

Upvotes: 0

Views: 717

Answers (2)

Levent Dag
Levent Dag

Reputation: 170

Ok wow thanks guys. I was so desperate on solving this problem. Thanks for all answers and especially to @Stargateur and @user3629249.

Ok so there are 2 things i needed to be made clear of and thankfully with you guys i get it now.

  1. I needed to use the right filedescriptors, which seems obvious, but i always assumed to use either connfd from connect/accept or the socket. However it turns out as user3629249 said, i the connect method on the clients side only tells wheter or not the connection is established but it isn't a descriptor. So the client needs to use the filedescriptor from the socket, in my example its sockfd.

On the Server side the descriptor to the client is given by accept, so i need to use in my case connfd.

  1. Beeing pedantic i already must use some strict flags because of my uni, however using even more strict flags is really useful. I always am and was very strict in Java but forgot to apply this mentality also in C where it is even more of importance.

So here below i post my solution to this problem, and again thanks guys! You made my day! For sake of simplicity the header file isn't changed, however there are still somethings left in this code also. If you use something similar please error check on everything, like the recv and send methods and please read the comments from Stargateur and user3629249, it will help you to understand why my code failed and now works.

Makefile

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

Client

#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();
}

Server

#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();
}

So the output is as expected. From ./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...

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

Upvotes: 0

Stargateur
Stargateur

Reputation: 26767

You have some minors issue and some big issue:

Small:

  • You don't affect ai_protocol in hints, but it's require by getaddrinfo() when hints is not NULL
  • You use the return of connect() as a file descriptor but it's a error code number. You must use the file descriptor return by socket() in a client, or by accept() in a server.
  • You use the wrong file descriptor when you use send() in the server. You SHOULD not send data on the file descriptor that handle connection in a SOCK_STREAM socket type.
  • getaddrinfo() return a list, you should iterate on it, but you assume that there is always one result, but they could be zero or more than one.
  • hints is initialized, you invoke undefined behavior with memset() because sizeof(struct addrinfo) > sizeof(struct addrinfo *) so you are out of bound when you write zero on ai (GLOBAL... GLOBAL EVERYWHERE)

Big:

  • You use global without reason
  • You declare more than one variable by line.
  • You use global without reason
  • You don't verify all your system call error !
  • You use global without reason

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

Upvotes: 1

Related Questions