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基础循环结构
int x=10; do { System.out.println("value of x:"+x); x++; } while(x<20); //do while循环 1 ...
- Redis常用命令(3)——Hash
HDEL 格式:HDEL key field [field ...] 作用:删除哈希表中的一个或多个域. 返回值:删除的域的个数. HEXISTS 格式:HEXISTS key field 作用:判断 ...
- springboot data jdbc 数据库日期和查询出来的结果不一致
解决方法: 一.将serverTimezone=UTC改为CTT url: jdbc:mysql://localhost:3306/moviechoicesystem?serverTimezone=C ...
- Redux学习day1
01.React介绍 Redux是一个用来管理管理数据状态和UI状态的JavaScript应用工具.随着JavaScript单页应用(SPA)开发日趋复杂,JavaScript需要管理比任何时候都要多 ...
- 【SpringBoot】07.SpringBoot文件上传
SpringBoot文件上传 1.编写html文件在classpath下的static中 <!DOCTYPE html> <html> <head> <met ...
- ubuntu下创建http服务器
使用ubuntu搭建一个简单的http服务器安装apache21.sudo apt-get update2.sudo apt-get install apache2 安装成功后,再/etc/apach ...
- 6. Spark SQL和Beeline
*以下内容由<Spark快速大数据分析>整理所得. 读书笔记的第六部分是讲的是Spark SQL和Beeline. Spark SQL是Spark用来操作结构化和半结构化数据的接口. 一. ...
- Centos7上一次War包的部署与运行
Centos7上一次War包的部署与运行 前言 由于前段时间第一次部署一个小型的项目,时间一长所以有些步骤有时候时间一长就忘了,在此做个简单的记录 一.原始系统开发环境 操作系统:Windows10: ...
- nginx&http 第三章 ngx http ngx_http_process_request_headers
HTTP 请求行正确处理完成后,针对 HTTP/1.0 及以上版本紧接着要做的就是请求 HEADER 的处理与解析了 /** * 用于处理http的header数据 * 请求头: * Host: lo ...
- tcp 客户端 发送syn
简介 sys_connect->inet_stream_connect->inet_stream_connect->tcp_v4_connect->tcp_connect对于t ...