c - c 中的 TCP 聊天无法正确读取/发送字符串

标签 c string sockets tcp

我正在尝试实现多客户端聊天,使用基于 TCP 套接字的连接和线程来管理客户端和输入。但是,发送或接收的字符串的读写没有正确执行,我不太明白为什么。

服务器:

#define IP "127.0.0.1"
#define PORT 9000
#define MAX_CONNECTION_Q 10
#define CLIENTS 10
#define BUFFSIZE 1024
#define NAMELEN 32
#define COMMANDS 16
#define MESSAGESIZE 256
#define LINENUM 512

struct chat{
  char userlist[CLIENTS][NAMELEN];
  char admin[NAMELEN];
  char ID[NAMELEN];
  char logg[512][216];
  struct chat *next;
  int pos;
};

struct client_info{
  pthread_t client_ID;
  int sockfd;
  struct chat chats;
  char user[NAMELEN];
  struct client_info *next;
};

struct header{
  struct client_info *fnode, *lnode;
}*client_head;

struct chat_head{
  struct chat *fnode,*lnode;
}*chat_head;


int sockfd, clinfo;
struct client_info thread_info[CLIENTS];
struct header client_list;
pthread_mutex_t mutex;

void list_init() {
  client_head = malloc(sizeof(struct header));
  chat_head = malloc(sizeof(struct chat_head));
  client_head->fnode = NULL;
  chat_head->fnode = NULL;
}

int compare(int a, int b){
  return (a-b);
}

void chat_insert(struct chat *node){
  if(chat_head->fnode == NULL){
    chat_head->fnode = node;
    chat_head->lnode = node;
  }
  else{
    chat_head->lnode->next = node;
    chat_head->lnode = node;
  }
}

int search_cli(char *name){
  struct client_info *tmp = client_head->fnode;
  while(tmp!=NULL){
    if(strcmp(tmp->user,name) == 0){
      return tmp->sockfd;
    }
    tmp = tmp->next;
  }
  return -1;
}

void client_insert(struct client_info *node){
  if(client_head->fnode == NULL){
    client_head->fnode = node;
    client_head->lnode = node;
    node->next = NULL;
  }
  else{
    client_head->lnode->next = node;
    client_head->lnode = node;
    node->next = NULL;
  }
}

void client_delete(struct client_info *node){
  struct client_info *last = client_head->fnode;
  struct client_info *tmp = last->next;
  if(client_head->fnode == NULL){
    return;
  }
  if(compare(last->sockfd, node->sockfd) == 0){
    client_head->fnode = tmp;

    free(last);

    if(client_head->fnode == NULL){

      client_head->lnode = NULL;

    }

    return;
  }
  while(tmp!=NULL){
    if(compare(tmp->sockfd, node->sockfd) == 0) {
      last->next = tmp->next;

      free(tmp);
      return;
    }

    tmp = tmp->next;
  }

}

void display_clients(){
  struct client_info *tmp = client_head->fnode;
  while(tmp!=NULL){
    printf("Username: %s\nSocket: %d\n--------------------\n",tmp->user,tmp->sockfd);
    tmp = tmp->next;
  }
}

void change_username(int sock,char *sender){
  struct client_info *tmp = client_head->fnode;
  while(tmp!=NULL){
    if(tmp->sockfd == sock){
      strcpy(tmp->user,sender);
      break;
    }
    tmp = tmp->next;
  }
}

void *client_commands_handler(void *fd){
  struct client_info *clinfo = (struct client_info *)fd;
  char *buffer = malloc(NAMELEN);
  char receiver[NAMELEN],sender[NAMELEN],message[BUFFSIZE];
  recv(clinfo->sockfd,buffer,NAMELEN,0);
  strcpy(clinfo->user,buffer);
  memset(buffer,0,sizeof(buffer));
  int nbytes;
  while(1){
    memset(buffer,0,sizeof(buffer));
    nbytes = recv(clinfo->sockfd, buffer, BUFFSIZE, 0);
    if(nbytes<=0) {
      printf("%s lost connection\n", clinfo->user);
      pthread_mutex_lock(&mutex);
      client_delete(clinfo);
      pthread_mutex_unlock(&mutex);
      break;
    }
    printf("%s\n",buffer);
    else if(strncmp(buffer, "private", 7) == 0) {
      int sockid;
      sscanf(buffer,"private %s %s %s",sender,receiver,message);
      printf("%s%s%s",sender,receiver,message);
      if((sockid = search_cli(sender)) == -1){
        char *server = malloc(NAMELEN);
        strcpy(server,"<Server> User doesn't exist\n");
        write(clinfo->sockfd,server,strlen(server));
      }
      else{
        struct chat new_chat;
        new_chat.pos = 0;
        strcpy(new_chat.logg[new_chat.pos],message);
        new_chat.pos++;
        strcpy(new_chat.userlist[0],sender);
        strcpy(new_chat.userlist[1],receiver);           
      sprintf(new_chat.ID,"%s%s",new_chat.userlist[0],new_chat.userlist[1]);
        new_chat.next = NULL;
        pthread_mutex_lock(&mutex);
        chat_insert(&new_chat);
        pthread_mutex_unlock(&mutex);
        send(sockid,message,strlen(message),0);
      }
    }
    else {
      fprintf(stderr, "Garbage data from [%d] %s...\n", clinfo->sockfd, clinfo->user);
    }
  }
  close(clinfo->sockfd);
  return NULL;
}

void *server_commands_handler() {

}

int main() {
  int size,clientfd;
  struct sockaddr_in server_addr, client_addr;
  pthread_t server_commands;
  list_init();
  pthread_mutex_init(&mutex, NULL);
  if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    perror("Couldn't get server socket");
    exit(0);
  }
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(PORT);
  server_addr.sin_addr.s_addr = inet_addr(IP);
  memset(&(server_addr.sin_zero), 0, 8);
  if(bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
    perror("Couldn't bind socket");
    exit(0);
  }
  if(listen(sockfd, MAX_CONNECTION_Q) == -1){
    perror("Couldn't listen");
    exit(0);
  }
  if(pthread_create(&server_commands, NULL, *server_commands_handler, NULL) != 0){
    perror("Couldn't create thread");
    exit(0);
  }
  while(1){
    size = sizeof(struct sockaddr_in);
    if((clientfd = accept(sockfd, (struct sockaddr *)&client_addr,(socklen_t*)&size))==-1){
      perror("Connection failed");
      exit(0);
    }
    printf("Client accepted\n");
    struct client_info clinfo;
    clinfo.sockfd = clientfd;
    clinfo.next = NULL;
    pthread_mutex_lock(&mutex);
    client_insert(&clinfo);
    pthread_mutex_unlock(&mutex);
    pthread_create(&clinfo.client_ID, NULL, client_commands_handler, (void *)&clinfo);
  }
  return 0;
}

客户:

#define SERVERIP "127.0.0.1"
#define SERVERPORT 9000
#define BUFFSIZE 1024
#define USERLEN 32
#define OPTLEN 16

struct threadinfo{
  pthread_t thread_ID;
  int sockfd;
};

struct USER{
  char user[USERLEN];
  int sockfd;
  char current_convo[USERLEN];
};

int isconnected, sockfd, sent;
char option[BUFFSIZE];
struct USER me;

int connect_with_server();
void logout(struct USER *me);
void login(struct USER *me);
void *receiver();

void login(struct USER *me){
  if(isconnected){
    printf("Already connected\n");
    return;
  }
  sockfd = connect_with_server();
  if(sockfd == -1){
    perror("Couldn't connect to server\n");
  }
  if(sockfd >= 0) {
    isconnected = 1;
    me->sockfd = sockfd;
    printf("Logged in as %s\n", me->user);
    struct threadinfo thread;
    pthread_create(&thread.thread_ID, NULL, receiver, (void *)&thread);
  }
  else {
    printf("Couldn't connect.\n");
    exit(0);
  }
}

void *receiver(){
  int recvd;
  char msg[BUFFSIZE];
  while(isconnected){
    memset(msg,0,sizeof(msg));
    recvd = read(sockfd, msg, sizeof(msg));
      if(!recvd) {
        printf("Connection Lost from Server\n");
        isconnected = 0;
        close(sockfd);
        break;
      }
      if(recvd > 0) {
        fputs(msg,stdout);
      }
  }
  return NULL;
}

int main() {
    memset(&me, 0, sizeof(struct USER));
    char msg[BUFFSIZE];
    char *token;
    while(fgets(option,BUFFSIZE-1,stdin)){
      memset(msg,0,sizeof(msg));
      if(strncmp(option,"exit", 4) == 0){
        logout(&me);
        break;
      }
      else if(strncmp(option, "login", 5) == 0){
        token = strtok(option, " ");
        token = strtok(NULL, "\n");
        memset(me.user, 0, sizeof(char)*USERLEN);
        if(token != NULL) {
          strcpy(me.user, token);
          login(&me);
        }
        else{
          printf("Couldn't get valid username\n");
        }
      }
      else if(strncmp(option, "private", 7) == 0){
        if(isconnected == 0){
          printf("User not connected\n");
        }
        else{
          char *tmp = malloc(USERLEN);
          token = strtok(option, " ");
          token = strtok(NULL, " ");
          strcpy(tmp,token);
          token = strtok(NULL, "\n");
          sprintf(msg,"private %s %s %s",me.user,tmp,token);
          send(sockfd, msg, strlen(msg),0);
        }
      }
  return 0;
}

该代码有很多代码对于简单的基于双向输入的连接来说是不必要的,但是从长远来看,我正在尝试实现它,它将具有更多功能。这里有很多事情需要修复,但我的问题是主要侧重于通过“私有(private)”输入发送字符串,应如下所示:

private (username of receiver) (message to send)

最佳答案

我还在看它,但我注意到了一些事情。在您的服务器中,您有

  char *buffer = malloc(NAMELEN);
  char receiver[NAMELEN],sender[NAMELEN],message[BUFFSIZE];
  recv(clinfo->sockfd,buffer,NAMELEN,0);
  strcpy(clinfo->user,buffer);
  memset(buffer,0,sizeof(buffer));
  int nbytes;
  while(1){
    memset(buffer,0,sizeof(buffer));
    nbytes = recv(clinfo->sockfd, buffer, BUFFSIZE, 0);

缓冲区变量接收指向 NAMELEN (32) 字节 block 的指针。您无需查看状态即可接收 NAMELEN 字节。到目前为止,一切都很好。然后清除缓冲区的第一个 sizeof 字节(由于缓冲区是一个指针,因此您将清除 4 或 8 个字节)。不久之后,您将 BUFFSIZE (1024) 字节接收到可怜的 32 字节缓冲区中。

我建议您通过 telnet 测试来运行服务器

telnet 127.0.0.1 9000

并输入您的程序所期望的内容。请记住,只有当您按回车键时才会传输数据。

客户端可以通过 netcat 进行类似的练习:

nc -l 127.0.0.1 9000

当我这样做时,为什么你的客户端是多线程的?如果直接调用receiver()而不是创建另一个线程,事情会变得简单。

还要留意您的 malloc 并确保它们与 free 配对。我看到很多地方都分配了固定大小的内存块,然后不释放它。使用局部变量可以更干净地完成此操作。当你回来时,这些就会消失。或者,查看 alloca(),当您返回时它也会消失。它并非随处可用,但非常有用。

关于c - c 中的 TCP 聊天无法正确读取/发送字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47688791/

相关文章:

c - 为什么我不能使用按位异或来交换字符串中的字符?

c# - String.StartsWith 无法正常工作

java - 通过Socket传输大数据

android - 使用套接字从Android到NodeJS服务器的多个参数

php - 从 PHP 脚本绑定(bind)到套接字时遇到问题

c - if 语句中的自增运算符

使用带空格的字符串创建带有输入用户的文件 (C)

c - Fortran/MPI 非持久数据的非阻塞发送

javascript - 字符串操作: Find the discount of a price/number while in a String in JavaScript

c++ - 想要分配 std::string 但编译器认为是 bool