программирование linux C клиент-сервер.Клиент не получает данные.локальный - PullRequest
0 голосов
/ 13 июня 2019

Запускаю клиент-серверную прогу на localhost. Моя программа должна отправить список файлов локального каталога с сервера клиенту, а клиент должен распечатать его. Клиент случайным образом печатает некоторые имена файлов (не все) или не печатает их вообще.

если я изменяю nc для клиента (nc localhost 21) или сервера (sudo nc -l 21), все работает.

клиент:



#ifndef DEBUG
#define DEBUG
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>


#ifndef SIZE
#define SIZE 1000
#endif

#define SERVER_PORT 21

#define CLIENT_IP 0
#define SERVER_IP 0

void recv_smth2(int skt);
int connect2(int IP, int PORT);


int main(){
  int port = htons(SERVER_PORT);
  int sck =  connect2(SERVER_IP,port);
  if (sck == -1){
    return 0;
  }
  recv_smth2(sck);
}

void recv_smth2(int skt){
  char BUF[SIZE];
  while(1){
    int l = recv(skt,BUF,SIZE-1,0);
    if(l == -1){
      perror("recv error");
    }
    BUF[l] = 0;
    printf("%s", BUF);
  }
}

int connect2(int IP, int PORT){
  //^ in network byte order^


  struct sockaddr_in addr;

  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = PORT;
  addr.sin_addr.s_addr = IP;

  int skt = socket( PF_INET, SOCK_STREAM, 0 );
  if (skt == -1){
    perror("socket error");
  }
  int cnct = connect( skt, (struct sockaddr*) &addr, sizeof(addr) );
  if (cnct == -1){
    perror("cnct error");
    return -1;
  }
  return skt;
}



Сервер:

#define SERVER_PORT 21

#ifndef SIZE
#define SIZE 1000
#endif


#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<arpa/inet.h>

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>

#include<errno.h>

#include<dirent.h>
#include<sys/stat.h>

int open_port(int PORT);
void send_string(const char *s,int n,int sk);
void send_file_list(int skt);

int main(){
  int port = htons(SERVER_PORT);
  int sk = open_port(port);
  if (sk == -1){
    perror("can't open port");
  }

  send_string("Hello!\n", sizeof("Hello!\n"), sk);
  send_file_list(sk);

 }



void send_file_list(int skt){
  char buf[SIZE];
  DIR *dir;
  struct dirent *ent;
  if((dir = opendir(".")) != NULL ){
      while ((ent = readdir(dir)) != NULL){
            int n = strnlen(ent->d_name, SIZE - 2);//without \0
            snprintf(buf,n+2,"%s\n",ent->d_name);//\n\0
            send_string(buf,n+2,skt);
      }
    }
}

void send_string(const char *s, int n, int sk){
  send(sk,s,n,0);
}

// all in network byte order
int open_port(int PORT){
  struct sockaddr_in addr;

  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  //addr.sin_port = htons(PORT);
  addr.sin_port = PORT;

  int skt = socket(PF_INET, SOCK_STREAM, 0);

  int enable = 1;
  if(setsockopt(skt,SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)))
     perror("setsockopt(SO_REUSEADDR)failed");
  bind(skt, (struct sockaddr*) &addr, sizeof(addr));

  if(listen(skt, 0) == -1){
    perror("listen ");
    return -1;
  }
  struct sockaddr_storage addr_s;

  memset(&addr_s, 0, sizeof(addr_s));

  int addr_size = sizeof(addr_s);
  int fd = accept( skt, (struct sockaddr*) &addr_s, &addr_size);
  if (fd == -1){
    perror("accept");
  }

  if (fd != 0){
  }
  return fd;  

}

компилировать

gcc client.c -o client
gcc server.c -o server
run:
$ sudo ./server
$ client 

Клиент должен вывести «Hello», а затем список всех файлов в каталоге сервера. Он печатает только «Hello», затем случайным образом печатает некоторые имена файлов из каталога сервера. Если я использую nc вместо клиента, я каждый раз получаю имя каждого файла.

1 Ответ

3 голосов
/ 13 июня 2019

Ваш клиент получает все имена файлов, вы просто игнорируете некоторые из них.

API сокетов не гарантирует, что каждый вызов recv будет производить содержимое ровно одного вызова send. Содержимое может быть разделено так, что вам придется многократно вызывать recv, или объединять вместе, чтобы вы получили несколько имен файлов одновременно.

Последнее происходит с вами: буфер, который вы заполняете в клиенте recv, содержит несколько имен файлов, например: server.c\n\0client\n\0client.c\n\0 и так далее. Когда вы передаете это printf, только содержимое буфера интерпретируется как строка, что означает, что printf останавливается на первом \0, а остальные имена файлов игнорируются.

Вы можете перебирать полученное содержимое, пока не достигнете длины l, и распечатать каждый символ, который не является \0. Следующий код делает это, но, возможно, не самым эффективным способом:

for (int i = 0; i < l; i++) {
    if (BUF[i] != '\0')
        putchar(BUF[i]);
}

Вы также можете использовать цикл с strlen и puts, или что-то подобное.

...