TCP聊天无法正确读取/发送字符串

时间:2017-12-07 06:18:50

标签: 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;
}

代码有很多o代码对于简单的基于双向输入的连接是不必要的,但是它将在我试图实现的长期内有更多的功能。这里有很多东西要解决,但我的问题是主要集中在通过“私人”输入发送字符串,应该如下:

  

私人(接收者的用户名)(要发送的消息)

1 个答案:

答案 0 :(得分:0)

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

  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)字节块的指针。你在不查看状态的情况下重新获取NAMELEN字节。到现在为止还挺好。然后清除第一个sizeof缓冲区字节(因为缓冲区是一个指针,你正在清除4或8个字节)。不久之后,你将BUFFSIZE(1024)字节恢复到糟糕的32字节缓冲区。

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

telnet 127.0.0.1 9000

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

客户端可以使用netcat进行类似的操作:

nc -l 127.0.0.1 9000

虽然我在这,但为什么你的客户端多线程?如果直接调用receiver()而不是创建另一个线程,它会简化操作。

还要留意你的mallocs并确保它们与免费配对。我看到你在malloc一个固定大小的内存块的许多地方然后不释放它。使用局部变量可以实现更多清洁。当你回来时,那些消失了。或者,查看alloca(),当你返回时它也会消失。它无处不在,但非常有用。