0

pthread を使用して、かなり単純な C チャット サーバーを作成しました。サーバーは問題なく動作し、クライアントは telnet を使用してサーバーに接続し、クライアントは相互に通信できます。各クライアントは、独自のスレッド内で開始されます。クライアントのいずれかが切断することを望む場合、問題が発生します。私はクライアントソケットを閉じてそのスレッドを終了しますが(そうするか、少なくともgdbはそう言っています)、他のスレッドがまだ実行されているにもかかわらず、この障害は何らかの形でサーバー側のすべての通信を閉じます. この後、クライアントは相互に通信できなくなり、サーバーがまったく役に立たなくなります。これを修正する方法がわかりません。あなたの助けを求めます。ありがとうございました。

これが私のコードです。私の間違いがどこにあるかわからないので、完全なコードを含めます

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> 
#include <string.h> 
#include <errno.h>  
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>

int user[30];     // sockets of connected users
char names[30][20];
int user_count=0; // number of active connections
int listen_sock;  // main socket, recieves connections
char buffer[500];
pthread_t *thready[30];
pthread_mutex_t mutex;


struct arg_struct {
    int arg1;
    int arg2;
};

void sending(char *text, int i) {
  char buffer2[504];
  strcpy(buffer2, "\r\0");
  strcat(buffer2, text);
  strcat(buffer2, "\r\0");
  write(user[i], buffer2, strlen(buffer2));
}

void welcome(int id) {
  int len;
  int sock;
  char name[20];
  pthread_mutex_lock(&mutex);
  sock = user[id];

  write(sock,"\rEnter your name: ", 18);
  // Read returns number of characters recieved
  len = read(sock,name,20);
  // Add zero to the end of string (read doesnt do it)
  name[len]='\0';

  while (name[len] < 32) 
  { 
    name[len]='\0';
    len--;
  }

//  snprintf(buffer,"Welcome %s!\n",name);
  write(sock, "Welcome\r\n", 7);
  strcpy(names[id], name);
  sending(buffer,id);
  pthread_mutex_unlock(&mutex);
}

void echo(char *text) {
  int i; 
  for (i=0; i<user_count; i++) {
    sending(text, i);
  }
}

void disconnect(int id) {
  int i;
  char deadname[20];

  strcpy(deadname,names[id]);
  sending("You have been disconnected\n",id);
  shutdown(user[id], SHUT_RDWR);
  close(user[id]);
  for (i=id; i<user_count-1; i++) { 
    user[i]=user[i+1];
    strcpy(names[i],names[i+1]);
  }
  user_count--;
  sprintf(buffer,"%s has disconnected\n",deadname);
  echo(buffer);
}

void finish() {
  echo("Shuting down, disconnecting everyone!\n");
  while (user_count > 0) disconnect(user_count-1);
  shutdown(listen_sock, SHUT_RDWR);
  close(listen_sock);
  pthread_mutex_destroy(&mutex);
  exit(0);
}

void getcomm(char *inpstr, char *comm) { /* First letter from inpstr goes to comm,
                                            and is removed from inpstr */
  int wpos=0,leng;                      /* Splits first word from the rest of the sentence */
  char *zal;

  zal=inpstr;
  leng=strlen(zal);

  while (*inpstr>32 && wpos<14) {
    *comm=*inpstr++;
    comm++;
    wpos++;
  }
  inpstr=(char *)memmove(zal,inpstr+1,leng);
  *comm='\0';
}

void input(int id) {

}

void* communication(void* par){
    int sock = ((int*)par)[0];
    int id = ((int*)par)[1];
    free((int*)par);
    welcome(id);
    while(1){
      char line[500];
      char command[20];
      char name2[16];
      int len,sock;

      // 1. read line from user
      len=read(user[id],line,499);
      pthread_mutex_lock(&mutex);
      line[len]='\0';
      if (len == 0) {  // User has terminated the connection
        continue;
      }

      while (line[len] < 32) { // Remove special chars
        line[len]='\0';
        len--;
      }

      // 2. Get first word
      getcomm(line,command);

      // 3. Recognise command and execute it
      len=strlen(command);
      if (strncmp(command,"quit",4) == 0) {
        disconnect(id);
        pthread_exit(NULL);
      }
      else if (!strncmp(command,"tell",len)) {
        getcomm(line,name2);
        sprintf(buffer,"%s tells %s: %s\n", names[id], name2, line);
        echo(buffer);
      }
      else if (!strncmp(command,"shut",len)) {
        finish();
      }
      // 4. Unknown command
      else {
        sprintf(buffer,"%s tells %s %s\n", names[id], command, line);
        echo(buffer);
      }
      pthread_mutex_unlock(&mutex);
    }
}

int main( int argc, char *argv[] ) {
  int pid;
  struct sockaddr_in bind_addr;
  struct sockaddr_in acc_addr;
  int size, user_id, i, sel,on;
  int port_number;
  char * eptr = NULL;
  fd_set readmask;
  pthread_mutexattr_t attr;
  pthread_mutexattr_init(&attr);
  pthread_mutex_init(&mutex, NULL);

  listen_sock = socket(AF_INET, SOCK_STREAM, 0);
  if (listen_sock==-1) {
    perror("socket()");
    pthread_mutex_destroy(&mutex);
    exit(-1);
  }
  on = 1;
  if (argc == 1){
    perror("Please specify port number");
    pthread_mutex_destroy(&mutex);
    //exit(0);
  } 
  if (argc == 2){
    port_number = (int) strtol(argv[1], &eptr, 10);
    if (*eptr != '\0'){
      perror("Invalid Port Number!");
        pthread_mutex_destroy(&mutex);
      exit(-1);
    }
  }
  port_number = 7501;

  bind_addr.sin_family = AF_INET;
  bind_addr.sin_addr.s_addr = INADDR_ANY;
  bind_addr.sin_port = htons(port_number);
  size=sizeof(struct sockaddr_in);
  setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));

  if (bind(listen_sock, (struct sockaddr *)&bind_addr, size)==-1) {
    perror("bind()");
    pthread_mutex_destroy(&mutex);
    exit(-1);
  }

  listen(listen_sock, 10);

  while(1) {
      FD_ZERO(&readmask);
      FD_SET(listen_sock, &readmask);
      if (FD_ISSET(listen_sock, &readmask)) {
          user_id = user_count++;
          int * soc = (int*)malloc(2*sizeof(int));

          soc[0] = accept(listen_sock,(struct sockaddr *)&acc_addr, &size);
          soc[1] = user_id;
          pthread_mutex_lock(&mutex);
          user[user_id] = soc[0];

          pthread_mutex_unlock(&mutex);

          if( ( pthread_create( &thready[user_id], NULL, communication, (void*)soc ) ) < 0)
                {
                    perror("could not create thread");
                    return 1;
                } 
            echo(buffer);
          continue;
    }
  }

  pthread_mutex_destroy(&mutex);
  return 0;
}
4

1 に答える 1

1

pthread_exit() を呼び出す前にミューテックスのロックを解除していないようです。これにより、他のすべての通信スレッドがブロックされます。

また、FD_SET() の後で select() 呼び出しを見逃していませんか?

于 2013-01-15T18:39:49.943 に答える