C语言实现聊天室(windows版本)
来源:微信公众号「编程学习基地」
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 256
int 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); /*设置受信*/
}
遇到的问题
等待线程返回的过程中最先用的是WaitForSingleObject
,很遗憾这是个阻塞函数,直到线程执行完成返回之后才会继续往下执行,所以后面通过WaitForMultipleObjects
这个windowsAPI调用对hThread线程数组进行线程等待释放。
整个过程不算太难,主要是仅仅实现了群聊功能,所以只需要了解windows下的网络编程以及多线程编程和线程的同步方法就可以实现这个样一个功能。
源代码后台发送关键字C语言聊天室获取,socket网络编程方法可通过上期C语言实现web服务器学习,多线程以及线程的同步可通过阅读全文在我的个人网站里面
C语言实现聊天室(windows版本)的更多相关文章
- Python实现网络图形化界面多人聊天室 - Windows
Python实现网络图形化界面多人聊天室 - Windows 项目名称:网络多人聊天室图形界面版本 项目思路: server.py 服务端文件,主进程中,创建图形化界面,询问地址(主机名,端口),点击 ...
- 奇妙的go语言(聊天室的开发)
[ 声明:版权全部,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] 这是一篇关于聊天室开发的博客,原来文章的地址来自于此.这篇文章非常具有代表性,对于代码中的函数 ...
- Python实现网络多人聊天室 - Windows
项目名称:多人聊天室项目结构: client.py server.py settings.py项目思路:服务端接收客户端连接,客户端发送信息给服务端,服务端将信息发送给所有客户端.项目实现:主进程负责 ...
- C语言实现聊天室软件
/* common.h */ /*服务器端口信息*/ #define PORTLINK ".charport" /*缓存限制*/ #define MAXNAMELEN 256 #d ...
- Golang语言快速上手到综合实战高并发聊天室
需要的联系我:QQ:1844912514 Go是Google开发的一种编译型,可并行化,并具有垃圾回收功能的编程语言.2015,Go迎来了全迸发的一年.时隔一年,回头再看,Go已跻身主流编程语言行列. ...
- 使用Go语言+Protobuf协议完成一个多人聊天室
软件环境:Goland Github地址 一.目的 之前用纯逻辑垒完了一个可登入登出的在线多人聊天室(代码仓库地址),这次学习了Protobuf协议,于是想试着更新下聊天室的版本. 主要目的是为了掌握 ...
- Python实现网络多人聊天室
网络多人聊天室 文件结构: chatroom ├── client.py # 客户端代码 ├── language.py # 语言文件 ├── server.py # 服务端代码 └── set ...
- 与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室
原文:与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...
- 与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室
原文:与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...
随机推荐
- Java到处运行的基础之 Class 文件
Java 实现一次编译到处运行的基础,来源于 Java 虚拟机屏蔽了操作系统的底层细节.使用 class 文件存储编译后的源程序,使得 Java 程序的编译与操作系统解耦.正是因为 Java clas ...
- Java学习的第五十天
1.求长方体的体积要求类中用但参数的方法去求 public class Cjava { public static void main(String[]args) { Box b1=new Box(1 ...
- ERP收付款的操作与设计--开源软件诞生22
赤龙ERP收款付款讲解--第22篇 用日志记录"开源软件"的诞生 [进入地址 点亮星星]----祈盼着一个鼓励 博主开源地址: 码云:https://gitee.com/redra ...
- input 与 button 的问题 (空隙/不等高/对不齐)及 解决办法
1. input 与 button 为什么有空隙? - 要明白为什么,需要了解一下几点基础知识(耐心看完,你会发现竟如此简单) 1. input 与 button 都属于行级块元素,都具有文本 ...
- C# type对象
新建控制台应用程序 新建一个类 class MyClass { private int id; private int age; public int numb; public string Name ...
- CEF避坑指南(一)——编译并自制浏览器
CEF即Chromium Embedded Framework,Chrome浏览器嵌入式框架.我们可以从自制浏览器入手,深入学习它.它提供了接口供程序员们把Chrome放到自己的程序中.许多大型公司, ...
- 09线程隔离的g对象
1,g是global的意思. g对象再一次请求中的所有的代码的地方,都是可以使用的. 同一次请求,那么在这个项目的所有地方都可以用了. from flask import Flask,request, ...
- Module Federation原理剖析
[转自团队掘金原文: https://juejin.im/post/6895324456668495880] 为什么需要学习webpack5 module Federation原理呢?因为EMP微前端 ...
- c语言: 错误:只允许在 C99 模式下使用‘for’循环初始化声明 用gcc编译出现
在gcc编译中如果使用 for(int i=0;i<n;++i){}会提示错误 错误:只允许在 C99 模式下使用'for'循环初始化声明 用gcc编译出现 就是说你的你的c编译器不是c99标准 ...
- 工具博客转载-ftrace
https://linux.cn/article-9273-1.html https://lwn.net/Articles/365835/ Documentation/trace/events.txt ...