多线程并发简单聊天室实现

多线程并发的简单聊天室实现

server:每一个clnt对应一个线程,将从线程接收到的消息都转发个所有已经建立连接的clnt,

clnt:一个线程用来发送msg给server,一个 线程用来接受msg并输出到stdout

server.c

使用了mutex建立临界区避免同时操作

#include <me.h>

#define BUF_SIZE 100
#define MAX_CLNT 256

void *handle_clnt(void*);//线程处理函数 
void  send_msg(char*,int);

int clnt_cnt = 0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutex;

int main(int argc,char *argv[])
{
  int serv_sock,clnt_sock;
  struct sockaddr_in serv_adr,clnt_adr;
  int clnt_adr_sz;
  pthread_t t_id;
  if (argc != 2)
  {
    printf("Usage : %s <port>\n",argv[0]);
    exit(1);
  }

  pthread_mutex_init(&mutex,NULL);
  serv_sock = socket(AF_INET,SOCK_STREAM,0);
  
  memset(&serv_adr,0,sizeof(serv_adr));
  serv_adr.sin_family = AF_INET;
  serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
  serv_adr.sin_port = htons(atoi(argv[1]));

  if (bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1)
    error_handle("bind() error")
  if (listen(serv_sock,5) == -1)
    error_handle("listen() error")

  while(1)//主进程只负责不断的建立连接,然后交给线程处理 
  {
    clnt_adr_sz = sizeof(clnt_adr);
    clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_adr,&clnt_adr_sz);

    pthread_mutex_lock(&mutex);
    clnt_socks[clnt_cnt++] = clnt_sock;
    pthread_mutex_unlock(&mutex);
    
    //用线程去处理逻辑 
    pthread_create(&t_id,NULL,handle_clnt,(void*)&clnt_sock);
    pthread_detach(t_id);//和主进程分离,但是会回收内存资源 
    printf("Connected client IP : %s \n",inet_ntoa(clnt_adr.sin_addr));//大端IP转string
  }
  close(serv_sock);
  return 0;
}

void* handle_clnt(void *arg)
{
  int clnt_sock = *((int*)arg);
  int str_len = 0,i;
  char msg[BUF_SIZE];

  //接受客户端发送过来的所有信息 
  while((str_len = read(clnt_sock,msg,sizeof(msg))) != 0)
    send_msg(msg,str_len);
  
  //清除无效连接 
  pthread_mutex_lock(&mutex);
  for (i=0; i<clnt_cnt; i++)
  {
    if (clnt_sock = clnt_socks[i])
    {
      while(i < clnt_cnt - 1)
      {
        clnt_socks[i] = clnt_socks[i+1];
        i++;
      }
      break;
    }
  }
  clnt_cnt--;
  pthread_mutex_unlock(&mutex);
  close(clnt_sock);
  return NULL;
}

void send_msg(char* msg,int len)//发送给所有建立连接的客户端 
{
  int i;
  pthread_mutex_lock(&mutex);
  for (i=0; i<clnt_cnt; i++)
    write(clnt_socks[i],msg,len);
  pthread_mutex_unlock(&mutex);
}

clnt.c

#include <me.h>
#define BUF_SIZE 100
#define NAME_SIZE 20

void *send_msg(void*);
void *recv_msg(void*);

char name[NAME_SIZE] = "[DEFAULT]";
char msg[BUF_SIZE];

int main(int argc,char *argv[])
{
  int sock;
  struct sockaddr_in serv_adr;
  pthread_t snd_thread,rcv_thread;
  void *thread_return;
  if (argc != 4)
  {
    printf("Usage : %s <IP> <port> <name>\n",argv[0]);
    exit(1);
  }

  sprintf(name,"[%s]",argv[3]);//将名字加入到待发送的buf中 
  sock = socket(AF_INET,SOCK_STREAM,0);

  memset(&serv_adr,0,sizeof(serv_adr));
  serv_adr.sin_family = AF_INET;
  serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
  serv_adr.sin_port = htons(atoi(argv[2]));

  if (connect(sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1)
    error_handle("connect() error")

  //创建发送和接受消息的线程 
  pthread_create(&snd_thread,NULL,send_msg,(void*)&sock);
  pthread_create(&rcv_thread,NULL,recv_msg,(void*)&sock);

  pthread_join(snd_thread,&thread_return);
  pthread_join(rcv_thread,&thread_return);
  close(sock);
  return 0;
}

void* send_msg(void *arg)
{
  int sock = *((int *)arg);
  char name_msg[NAME_SIZE + BUF_SIZE];
  while(1)
  {
    fgets(msg,BUF_SIZE,stdin);
    if (!strcmp(msg,"q\n") || !strcmp(msg,"Q\n"))
    {
      close(sock);
      exit(0);
    }
    sprintf(name_msg,"%s %s",name,msg);
    write(sock,name_msg,strlen(name_msg));
  }
  return NULL;
}

void* recv_msg(void *arg)
{
  int sock = *((int*)arg);
  char name_msg[NAME_SIZE + BUF_SIZE];
  int str_len;
  while(1)//服务器发送消息到所有客户端 
  {
    str_len = read(sock,name_msg,NAME_SIZE+BUF_SIZE-1);
    if (str_len == -1)
      return (void*)-1;
    name_msg[str_len] = 0;
    fputs(name_msg,stdout); //接受消息,打印到客户端 
  }
  return NULL;
}

使用回环地址测试没问题

把server.c放在云服务器上
  1. 在安全组里面开放你选定的端口

本地clnt运行时传入云服务器的公网IP+自己指定的端口,

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值