TCP/IP网络编程——多线程并发服务器的实现(构建多人聊天室)

话不多说,实现了多个客户端可以交换信息的简单聊天程序,程序如下:

chat_server.c

//多线程并发服务器端
//访问全局变量clnt_cnt和clnt_socks的代码将构成临界区
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>
#include <semaphore.h>

#define BUF_SIZE 100
#define MAX_CLNT 256

void *handle_clnt(void *arg);
void send_msg(char *msg, int len);
void error_handling(char *message);

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

int main(int argc, char *argv[])
{
  int serv_sock;
  int clnt_sock;
  
  struct sockaddr_in serv_addr;
  struct sockaddr_in clnt_addr;
  socklen_t clnt_addr_size;
  
  pthread_t t_id;
  
  if(argc!=2)
  {
    exit(1);
  }
  
  pthread_mutex_init(&mutex, NULL);	//初始化信号量
  //TCP socket
  serv_sock=socket(PF_INET, SOCK_STREAM, 0);
  if(serv_sock == -1)
    error_handling("socket error!");

  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;	//IPV4协议族
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  serv_addr.sin_port = htons(atoi(argv[1]));	//端口号

  if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
    error_handling("bind error");

  if(listen(serv_sock, 5) == -1)
    error_handling("listen error");

  while(1)
  {
    clnt_addr_size = sizeof(clnt_addr);
    clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
    
    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_join:调用该函数的线程将进入阻塞状态,等待结束后回收其资源
    //pthread_detach:调用该函数不会引起线程阻塞,线程结束后会自动释放所有资源
    pthread_detach(t_id);
    printf("connected client IP: %s \n", inet_ntoa(clnt_addr.sin_addr));
  }
  
  close(serv_sock);
  return 0;
}

void* handle_clnt(void* arg)
{
  int clnt_sock=*((int *)arg);
  int str_len=0;
  char msg[BUF_SIZE];
  
  while((str_len=read(clnt_sock, msg, sizeof(msg)))!=0)
    send_msg(msg, str_len);
  
  pthread_mutex_lock(&mutex);
  int i;
  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];
      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);
}

void error_handling(char *message)
{
  fputs(message, stderr);
  fputc('\n', stderr);
  exit(1);
}

chat_clnt.c

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

#define BUF_SIZE 100
#define NAME_SIZE 20

void * send_msg(void *arg);
void * recv_msg(void *arg);
void error_handling(char *message);

char name[NAME_SIZE]="[DEFAULT]";	//名字
char msg[BUF_SIZE];	//消息

int main(int argc, char *argv[])
{
  int sock;
  struct sockaddr_in serv_addr;

  pthread_t snd_thread, rcv_thread;
  void * thread_return;
  
  if(argc!=4)
  {
    exit(1);
  }
  sprintf(name, "[%s]", argv[3]);	//客户端名称
  
  //TCP socket
  sock=socket(PF_INET, SOCK_STREAM, 0);
  if(sock == -1)
    error_handling("socket error!");
  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
  serv_addr.sin_port = htons(atoi(argv[2]));

  if(connect(sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)
    error_handling("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;
}

void error_handling(char *message)
{
  fputs(message, stderr);
  fputc('\n', stderr);
  exit(1);
}

最终看一下我们的实现效果,注意编译时要加的后缀(多线程必须要添加),还是直接上图了


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值