首页 > 编程语言 > C语言实现简易网络聊天室
2021
09-30

C语言实现简易网络聊天室

本文实例为大家分享了C语言实现网络聊天室的具体代码,供大家参考,具体内容如下

业务逻辑:

1、客户端注册名字
2、告诉所有在线的客户端,XXX进入聊天室
3、新建一个线程为该客户端服务,随时接收客户端发送来的消息
4、当接收到一个客户端的消息时,向每一个客户端转发一份(群聊)
5、同时在线人数最多50人

任何客户端可以随意随时进入或退出客户端

服务端代码server.c

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

#ifndef DEBUG
 #define debug(format,...) {}
#else
 #define debug(format,...) \
 {\
  fprintf(stdout,"%s:%d:%s ",__func__,__LINE__,__TIME__);\
  fprintf(stdout,format,##__VA_ARGS__);\
  fprintf(stdout,"\n");\
 }
#endif//DEBUG

#define error(format,...)\
{\
 fprintf(stdout,"%s:%d:%s ",__func__,__LINE__,__TIME__);\
 fprintf(stdout,format,##__VA_ARGS__);\
 fprintf(stdout,":%m\n");\
 exit(EXIT_FAILURE);\
}

// 客户端最大连接数
#define CLIENT_MAX 50

// 服务器端口号
#define PORT 5566

// 缓冲区大小
#define BUF_SIZE 4096

// 重定义socket地址类型
typedef struct sockaddr* SP;

// 客户端结构体
typedef struct Client
{
 int sock;//socket 标识符
 pthread_t tid; //线程ID
 char name[20];
 struct sockaddr_in addr;
}Client;

// 定义50个存储客户端的结构变量
Client clients[50];

// 定义信号量用于限制客户端的数量
sem_t sem;

// 信号处理函数
void sigint(int num)
{
 for(int i=0; i<10; i++)
 {
  if(clients[i].sock)
  {
   pthread_cancel(clients[i].tid);//销毁线程
  }
 }
 debug("服务器退出!");
 exit(EXIT_SUCCESS);
}

void client_eixt(Client* client)
{
 sem_post(&sem);
 close(client->sock);
 client->sock = 0;
}

void client_send(Client* client,char* buf)
{
 size_t len = strlen(buf)+1;
 for(int i=0; i<CLIENT_MAX; i++)
 {
  if(clients[i].sock && clients[i].sock != client->sock)
  {
    send(clients[i].sock,buf,len,0);
  }
 }
}

void* run(void* arg)
{
 Client* client = arg;
 char buf[BUF_SIZE] = {};

 // 接收昵称
 int ret_size = recv(client->sock,client->name,20,0);
 if(0 >= ret_size)
 {
  client_eixt(client);
  return NULL;
 }

 // 通知其它客户端新人上线
 sprintf(buf,"!!!欢迎%s进入聊天室!!!",client->name);
 client_send(client,buf);
 for(;;)
 { 
  // 接收消息
  ret_size = recv(client->sock,buf,BUF_SIZE,0);
  if(0 >= ret_size || 0 == strcmp("quit",buf))
  {
   // 通知其它客户端退出
   sprintf(buf,"!!!%s退出聊天室!!!",client->name);
   client_send(client,buf);
   client_eixt(client);
   return NULL;
  }
  strcat(buf,":");
  strcat(buf,client->name);
  client_send(client,buf);
  debug(buf);
 }
}

int main(int argc,const char* argv[])
{
 signal(SIGINT,sigint);
 debug("注册信号处理函数成功!");

 sem_init(&sem,0,CLIENT_MAX);
 debug("初始化信号量成功!");

 int svr_sock = socket(AF_INET,SOCK_STREAM,0);
 if(0 > svr_sock)
 {
  error("socket");
 }
 debug("创建socket对象成功!");

 struct sockaddr_in svr_addr = {};
 svr_addr.sin_family = AF_INET;
 svr_addr.sin_port = htons(PORT);
 svr_addr.sin_addr.s_addr = INADDR_ANY;
 socklen_t addrlen = sizeof(svr_addr);
 debug("准备通信地址成功!");

 if(bind(svr_sock,(SP)&svr_addr,addrlen))
 {
  error("bind");
 }
 debug("绑定socket对象和通信地址成功!");


 if(listen(svr_sock,10))
 {
  error("listen");
 }
 debug("设置监听socket监听成功!");

 for(;;)
 {
  debug("等待客户端连接...");

  sem_wait(&sem);
  int index = 0;
  while(clients[index].sock)
  {
   index++;
  }

  clients[index].sock = accept(svr_sock,(SP)&clients[index].addr,&addrlen);
  if(0 > clients[index].sock)
  {
   kill(getpid(),SIGINT);
  }

  debug("有新的客户端连接,from ip:%s",inet_ntoa(clients[index].addr.sin_addr));
  pthread_create(&clients[index].tid,NULL,run,&clients[index]);
 }
}

客户端代码client.c

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

#ifndef DEBUG
 #define debug(format,...) {}
#else
 #define debug(format,...) \
 {\
  fprintf(stdout,"%s:%d:%s ",__func__,__LINE__,__TIME__);\
  fprintf(stdout,format,##__VA_ARGS__);\
  fprintf(stdout,"\n");\
 }
#endif//DEBUG

#define error(format,...)\
{\
 fprintf(stdout,"%s:%d:%s ",__func__,__LINE__,__TIME__);\
 fprintf(stdout,format,##__VA_ARGS__);\
 fprintf(stdout,":%m\n");\
 exit(EXIT_FAILURE);\
}

#define BUF_SIZE  4096
#define SERVER_PORT 5566
#define SERVER_IP  "192.168.0.125"
typedef struct sockaddr* SP;

void* run(void* arg)
{
 int cli_sock = *(int*)arg;
 char buf[BUF_SIZE] = {};
 for(;;)
 {
  int ret_size = recv(cli_sock,buf,BUF_SIZE,0);
  if(0 >= ret_size)
  {
   printf("服务器正在升级,请稍候登录!\n");
   exit(EXIT_SUCCESS);
  }
  printf("\r%30s\n>>>",buf);
  fflush(stdout);
 }
}


int main(int argc,const char* argv[])
{
 int cli_sock = socket(AF_INET,SOCK_STREAM,0);
 if(0 > cli_sock)
 {
  error("socket");
 }

 struct sockaddr_in cli_addr = {};
 cli_addr.sin_family = AF_INET;
 cli_addr.sin_port = htons(SERVER_PORT);
 cli_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
 socklen_t addrlen = sizeof(cli_addr);

 if(connect(cli_sock,(SP)&cli_addr,addrlen))
 {
  printf("服务器正在升级,请稍候登录!\n");
  return EXIT_SUCCESS;
 }

 char buf[BUF_SIZE] = {};
 printf("请输入你的?称:");
 gets(buf);
 send(cli_sock,buf,strlen(buf)+1,0);

 pthread_t tid;
 pthread_create(&tid,NULL,run,&cli_sock);

 for(;;)
 {
  printf(">>>");
  gets(buf);
  send(cli_sock,buf,strlen(buf)+1,0);
  if(0 == strcmp("quit",buf))
  {
   printf("退出聊天室!\n");
   return EXIT_SUCCESS;
  }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持自学编程网。

编程技巧