来源:微信公众号「编程学习基地」

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版本)的更多相关文章

  1. Python实现网络图形化界面多人聊天室 - Windows

    Python实现网络图形化界面多人聊天室 - Windows 项目名称:网络多人聊天室图形界面版本 项目思路: server.py 服务端文件,主进程中,创建图形化界面,询问地址(主机名,端口),点击 ...

  2. 奇妙的go语言(聊天室的开发)

    [ 声明:版权全部,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 这是一篇关于聊天室开发的博客,原来文章的地址来自于此.这篇文章非常具有代表性,对于代码中的函数 ...

  3. Python实现网络多人聊天室 - Windows

    项目名称:多人聊天室项目结构: client.py server.py settings.py项目思路:服务端接收客户端连接,客户端发送信息给服务端,服务端将信息发送给所有客户端.项目实现:主进程负责 ...

  4. C语言实现聊天室软件

    /* common.h */ /*服务器端口信息*/ #define PORTLINK ".charport" /*缓存限制*/ #define MAXNAMELEN 256 #d ...

  5. Golang语言快速上手到综合实战高并发聊天室

    需要的联系我:QQ:1844912514 Go是Google开发的一种编译型,可并行化,并具有垃圾回收功能的编程语言.2015,Go迎来了全迸发的一年.时隔一年,回头再看,Go已跻身主流编程语言行列. ...

  6. 使用Go语言+Protobuf协议完成一个多人聊天室

    软件环境:Goland Github地址 一.目的 之前用纯逻辑垒完了一个可登入登出的在线多人聊天室(代码仓库地址),这次学习了Protobuf协议,于是想试着更新下聊天室的版本. 主要目的是为了掌握 ...

  7. Python实现网络多人聊天室

    网络多人聊天室 文件结构: chatroom ├── client.py  # 客户端代码 ├── language.py  # 语言文件 ├── server.py  # 服务端代码 └── set ...

  8. 与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室

    原文:与众不同 windows phone (31) - Communication(通信)之基于 Socket UDP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

  9. 与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室

    原文:与众不同 windows phone (30) - Communication(通信)之基于 Socket TCP 开发一个多人聊天室 [索引页][源码下载] 与众不同 windows phon ...

随机推荐

  1. 深入理解golang:内存分配原理

    一.Linux系统内存 在说明golang内存分配之前,先了解下Linux系统内存相关的基础知识,有助于理解golang内存分配原理. 1.1 虚拟内存技术 在早期内存管理中,如果程序太大,超过了空闲 ...

  2. uniApp朋友圈(参考)

    介绍 功能:回复,点赞(笔芯),评论,图片(最多六张). 码云地址:https://gitee.com/sunliusen/friend 例:

  3. springcloud feign使用

    1.Spring cloud fein的继承特性 通过对接口的继承,能够实现对fein客户端和feign服务器代码量的减少. 2.Ribbon配置 {服务名}.ribbon.ConnectTimeOu ...

  4. 寻找性能更优秀的动态 Getter 和 Setter 方案

    反射获取 PropertyInfo 可以对对象的属性值进行读取或者写入,但是这样性能不好.所以,我们需要更快的方案. 方案说明 就是用表达式编译一个 Action<TObj,TValue> ...

  5. react中iconfont字体图标不显示问题

    如下图, 写四个圆圈,直接将iconfont的字体编码写在静态HTML结构中时显示没问题,然而明显这样的结构用循环写是更好的选择, 但是,页面上不能显示字体图片了,而是直接显示字体编码 原因是字体编码 ...

  6. 剑指Offer-Python(6-10)

    6.旋转数组的最小数字 class Solution: def minNumberInRotateArray(self, rotateArray): l = len(rotateArray) if l ...

  7. 学习笔记——ESP8266项目的例子编译时发生cannot find -lstdc++问题的解决

    在尝试对进行ESP8266项目的例子进行编译时发生cannot find -lstdc++问题 第一想法是安装libstdc++,结果安装时又发生了下面的情况: 再次查找原因,最后发现当前安装的交叉编 ...

  8. git 分支合并到master

    [参考:] https://segmentfault.com/q/1010000000181403  我们一般这样:远程创建一个主分支,本地每人创建功能分支,日常工作流程如下: # 去自己的工作分支 ...

  9. ubuntu12.04 安装lamp <1>

    安装:lamp: sudo apt-get install apache2 libapache2-mod-php5 php5-mysql mysql-server 删除mysql sudo apt-g ...

  10. 案例:简易的Div拖拽

    案例:简易的Div拖拽 鼠标移入Div区域后,按下鼠标左键,可以拖动Div移动;松开鼠标左键,Div拖动停止.同时要求Div不能拖出屏幕显示区域外. 拖拽原理:距离不变.三个事件(onmousedow ...