C语言实现简易聊天室

C语言聊天室

基于 tcp 实现群聊功能,本项目设计是在「windows环境下基于套接字(Socket)和多线程编程」进行开发的「简易聊天室」,实现了群聊功能,在VC6.0和VS2019运行测试无误。

运行效果

聊天室

分析设计

Windows下基于windows网络接口Winsock的通信步骤为「WSAStartup 进行初始化」--> 「socket 创建套接字」--> 「bind 绑定」--> 「listen 监听」--> 「connect 连接」--> 「accept 接收请求」--> 「send/recv 发送或接收数据」--> 「closesocket 关闭 socket」--> 「WSACleanup 最终关闭」

通信流程

了解完了一个 socket 的基本步骤后我们了解一下多线程以及线程的同步。

多线程

线程是进程的一条执行路径,它包含独立的堆栈和CPU寄存器状态,每个线程共享所有的进程资源。一个进程内的所有线程使用同一个地址空间,而这些线程的执行由系统调度程序控制,调度程序决定哪个线程可执行以及什么时候执行线程。「简而言之多线程是为了提高系统的运行效率。」

Win32 API下的多线程编程 也就是两个函数的应用创建线程CreateThread以及等待线程结束waitForSingleObject,具体案例这里不多做介绍。

线程的同步

每个线程都可以访问进程中的公共变量,资源,所以「使用多线程的过程中需要注意的问题是如何防止两个或两个以上的线程同时访问同一个数据,以免破坏数据的完整性」。数据之间的相互制约包括

1、「直接制约关系」,即一个线程的处理结果,为另一个线程的输入,因此线程之间直接制约着,这种关系可以称之为同步关系

2、「间接制约关系」,即两个线程需要访问同一资源,该资源在同一时刻只能被一个线程访问,这种关系称之为线程间对资源的互斥访问,某种意义上说互斥是一种制约关系更小的同步

windows线程间的同步方式有四种:「临界区、互斥量、信号量、事件。」

本项目是基于事件内核对象实现的线程同步,事件内核对象是一种抽象的对象,有受信和未授信两种状态,通过等待WaitForSingleObject实现线程同步。事件内核对象的使用流程如下:

「创建事件内核对象」

HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes, //安全属性
  BOOL                  bManualReset,  //是否手动重置事件对象为未受信对象
  BOOL                  bInitialState,  //指定事件对象创建时的初始状态
  LPCSTR                lpName    //事件对象的名称
);

「设置内核对象状态」

BOOL SetEvent(  HANDLE hEvent /*设置事件内核对象受信*/);BOOL ResetEvent(  HANDLE hEvent /*设置事件内核对象未受信*/);

「堵塞等待事件内核对象直到事件内核对象的状态为受信」

DWORD WaitForSingleObject(
  HANDLE hHandle,
  DWORD  dwMilliseconds
);

具体使用阅读全文在我的个人网站里看,篇幅太多。

服务端设计

在创建套接字绑定监听之后会有一个等待连接的过程,在接收到新连接之后,需要创建一个线程来处理新连接,当有多个新连接时可通过创建多个线程来处理新连接,

「定义最大连接数量以及最大套接字和最大线程」

#define MAX_CLNT 256int clnt_cnt = 0;   //统计套接字int clnt_socks[MAX_CLNT]; //管理套接字HANDLE hThread[MAX_CLNT]; //管理线程

「当有新连接来临的时候创建线程处理新连接」,并将新连接添加到套接字数组里面管理

hThread[clnt_cnt] = CreateThread(
 NULL,  // 默认安全属性
 NULL,  // 默认堆栈大小
 ThreadProc, // 线程入口地址(执行线程的函数)
 (void*)&clnt_sock,  // 传给函数的参数
 0,  // 指定线程立即运行
 &dwThreadId); // 返回线程的ID号
clnt_socks[clnt_cnt++] = clnt_sock;

线程的处理函数ThreadProc不做过多讲解,大致就是「一个服务器,多个客户端进行数据的接收以及群发」

主要讲解「线程同步」,当有多个新连接来临的时候,可能会造成多个线程同时访问同一个数据(例如clnt_cnt)。这个时候就需要线程的同步来避免破坏数据的完整性

首先是「创建一个内核事件」

HANDLE g_hEvent;   /*事件内核对象*/// 创建一个自动重置的(auto-reset events),受信的(signaled)事件内核对象g_hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);

然后再需要访问连接数量clnt_cnt这个变量之前进行「加锁(设置等待)」,访问完成之后「解锁(设置受信)」

/*等待内核事件对象状态受信*/
WaitForSingleObject(g_hEvent, INFINITE);
hThread[clnt_cnt] = CreateThread(NULL,NULL,ThreadProc,(void*)&clnt_sock,0,&dwThreadId);
clnt_socks[clnt_cnt++] = clnt_sock;
SetEvent(g_hEvent);    /*设置受信*/

通过套接字数组来进行数据的转发实现群聊功能,此时也用到了「线程同步」

void send_msg(char* msg, int len){ int i; /*等待内核事件对象状态受信*/ WaitForSingleObject(g_hEvent, INFINITE); for (i = 0; i < clnt_cnt; i++)  send(clnt_socks[i], msg, len, 0); SetEvent(g_hEvent);  /*设置受信*/}

客户端设计

同样也是在创建套接字连接到服务器之后,创建两个线程,一个线程实现数据的发送,一个实现数据的接收。

「发送数据到服务端」

DWORD WINAPI send_msg(LPVOID lpParam)
{
 int sock = *((int*)lpParam);
 char name_msg[NAME_SIZE + BUF_SIZE];
 while (1)
 {
  fgets(msg, BUF_SIZE, stdin);
  if (!strcmp(msg, 'q\n') || !strcmp(msg, 'Q\n'))
  {
   closesocket(sock);
   exit(0);
  }
  sprintf(name_msg, '[%s]: %s', name, msg);
  int nRecv = send(sock, name_msg, strlen(name_msg), 0);
 }
 return NULL;
}

「接收服务端数据并打印输出到显示器」

DWORD WINAPI recv_msg(LPVOID lpParam){ int sock = *((int*)lpParam); char name_msg[NAME_SIZE + BUF_SIZE]; int str_len; while (1) {  str_len = recv(sock, name_msg, NAME_SIZE + BUF_SIZE - 1, 0);  if (str_len == -1)   return -1;  name_msg[str_len] = 0;  fputs(name_msg, stdout); } return NULL;}

这样就不会阻塞等待终端输入之后再显示服务端发送过来的消息了。

遇到的问题

等待线程返回的过程中最先用的是WaitForSingleObject,很遗憾这是个阻塞函数,直到线程返回才会继续往下执行,所以后面通过WaitForMultipleObjects这个windowsAPI调用对hThread线程数组进行线程等待释放。

「缺陷:非高并发,对资源的利用不高,下周介绍Linux网络编程实现的聊天室,可能有新功能,敬请期待..」

整个过程不算太难,主要是仅实现了群聊功能,所以只需要了解windows下的网络编程以及多线程编程和线程的同步方法就可以实现这个样一个功能。

「源代码后台发送关键字windows聊天室获取」

socket网络编程方法可参考上期

C语言实现web服务器

多线程以及线程的同步可通过「阅读全文」在我的个人网站里面查阅。

关键字【聊天室】

(0)

相关推荐

  • 消息机制、子窗口和父窗口的消息传递

    消息机制、子窗口和父窗口的消息传递

  • 云数据库与简易聊天室[上]

    在2019版App Inventor中,云数据库组件被正式纳入到数据存储分组中,此前它一直被放在试验组件分组中.与本地数据库(tinyDB)及网络数据库(tinyWebDB)相比,云数据库组件具有更为 ...

  • 云数据库与简易聊天室[下]

    接上一篇文章<云数据库与简易聊天室[上]> 3.实现进入聊天室功能 现在,假设聊天室名称为"金属圈",让我们来回顾一下从首页到登录页,全局变量.组件属性及数据库存储内容 ...

  • 和福奇医生在同一个聊天室

    福奇医生是美国总统的首席医学顾问,也是美国"过敏和传染病研究所"的所长. 5月11日,福奇医生第一次来到语音社交媒体Clubhouse(聊斋)回答问题,和大家聊了一个小时. 福奇医 ...

  • swoole中websoket创建在线聊天室(php)

    swoole中websoket创建在线聊天室(php) swoole现仅支持Linix,macos 创建websocket服务器 首先现在服务器创建一个websocket服务器 <?php // ...

  • 优秀的语音聊天室app源码开发团队应具备的优势

    语音聊天室类的社交APP作为时下比较火热的程序,也斩获了很多有社交需求的用户,高流量高热度的背后也出现了很多想要做语音聊天APP的个人和公司,往往在开发初期不了解语音聊天程序开发的步骤都有哪些,在语音 ...

  • 蔡康永聊天室首秀,直播社交将引领未来趋势?

    ©深响原创 · 作者|李静林 熟悉的笑声.熟悉的语调,但这次出现的场景却有些特别. 身着黑白色搭配上衣的蔡康永,一改往日荧幕中鲜艳的穿搭,坐在了抖音聊天的直播间中.身边的还是他熟悉的搭档--小S徐熙娣 ...

  • 【聊天室2】你有什么故事想告诉我吗

    很多时候即使苦于无法忘记 但我也不忍心清空记忆 那些快乐想起时依然可以让我嘴角弯起 那些不开心现在想起来好像也没有那么沉重 那些想起却再也没联系的朋友 那些爱了就不后悔的爱情 那些让人喜极而泣的事情 ...

  • 【聊天室3】那段时光不能忘。

    挺多高三的 所以我明天发布完整版文 <关注高考这件'大'事> 之前因为好多人私信和留言问我 所以就在微博上剧透了一部分 不过我想有很多宝贝都是过来人 所以今日话题 #关于高考这件'大'事# ...