【c#开源下载】微风IM 增加局域网P2P通信 V2

Sunny5816 2015-02-19 04:03:24
加精
新年第一天 恭祝大家新年快乐

一直有朋友问P2P相关的问题,最近有时间在微风IM的基础上,实现了P2P通信,共享给大家,希望大家批评指正。

源码下载 数据库下载 数据库与第一版相同没有变化

我们知道在网络通信中,如果所有的通信都通过服务器转发,会增加服务器的负担,如果实现了P2P,客户端之间直接通讯,比如聊天或者传送文件时不再通过服务器,而是客户端之间直接通信,将会有效的减轻服务器的负担,提高程序的效率。

本节相关的P2P,指的是通过TCP协议,在局域网中实现的P2P,广域网中的P2P暂时没有涉及。

本Demo基于来自英国的networkComms2.3.1开源通信框架

工作原理-通过服务器,在客户端之间建立P2P通道,之后客户端之间的通讯可以脱离服务器

流程如下:

NetworkComms通信框架的内在通信机制,使得我们实现P2P通信非常的简单。

(1):服务器开始监听

(2) :客户端,开始连接服务器,然后也开始监听工作,其实成为一个服务器。连接的过程中,系统会给客户端随机分派一个端口,以便完成与服务器的通信。连接完成后,我们获取到客户端的IP和与服务器通信的端口,客户端在此端口上展开监听,也就是说每个客户端都会展开监听,具备作为服务器的所有特质。
模拟代码:       

ConnectionInfo connInfo = new ConnectionInfo("服务器IP", "服务器端口");
//客户端与服务器进行连接
Connection newTcpConnection = TCPConnection.GetConnection(connInfo);
//客户端与服务器连接成功后,开始监听本地端口,客户端也称为可以监听的服务器
TCPConnection.StartListening(connInfo.LocalEndPoint);

(3):每个客户端需要维护一个“P2P通信的连接”表

我们用一个静态类来实现,具体可查看Common类
//字典中存储 用户ID 和相应的连接引用
public static Dictionary<string, Connection> UserConnList = new Dictionary<string, Connection>();

public static void AddUserConn(string userID, Connection conn)
{
lock (dictLocker)
{
if (UserConnList.ContainsKey(userID))
{
UserConnList.Remove(userID);
}
UserConnList.Add(userID,conn);
}
}

public static bool ContainsUserConn(string userID)
{
lock (dictLocker)
{
if (UserConnList.ContainsKey(userID))
{
return true;
}
else
{
return false;
}
}
}

public static Connection GetUserConn(string userID)
{
lock(dictLocker)
{
if(UserConnList.ContainsKey(userID))
{
return UserConnList[userID];
}
else
{
return null;
}
}
}

public static void RemoveUserConn(string userID)
{
lock (dictLocker)
{
if (UserConnList.ContainsKey(userID))
{
UserConnList.Remove(userID);
}

}
}

4):客户端成功登陆后,从服务器获取所有在线其他客户端用户的本地端点(IP和端口)(即在其他客户端在步骤一中展开监听的端点),并进行连接

《1》客户端甲与其他客户端逐个进行连接,连接成功后,客户端甲添加对方用户ID和连接引用到本地P2P通道字典中

《2》客户端甲发送一个消息类型为”setupP2PMessage"的消息,给对方,以便于对方添加相应的记录到对方的P2P字典中

《3》客户端甲与其他用户进行连接时,客户端甲为“客户端”,其他的客户端为“服务器端”,所以在P2P通道的2端,总有一端为“客户端”,另一端为“服务器”。

配合NetworkComms通信框架,此种概念上的区分,并不影响P2P通道的通信。

客户端甲与其他客户端通信时,无论是作为”客户端“或者”服务器“均可,只要与对方存在TCP长连接即可。

《4》 这种由客户端之间彼此通信而建成的”服务器“,具备真正服务器的所有功能,会进行相应的”心跳检测“与”连接“维护等。

下面的代码:某客户端登陆后,获取所有已在线用户,并与之连接,连接完成后,发送”SetupP2PMessag"类型消息给对方。通过此过程,彼此双方的“P2P连接”都会建立完成。
private void GetP2PInfo()
{
//从服务器端,获取所有在线用户的信息 (用户ID,相对应的本地端点,在第一步中,客户端与服务器连接成功后,已经在此端点上开始监听了)
IList<UserIDEndPoint> userInfoList = Common.TcpConn.SendReceiveObject<IList<UserIDEndPoint>>("GetP2PInfo", "ResP2pInfo", 5000, "GetP2P");
//遍历所有的在线用户
foreach (UserIDEndPoint userInfo in userInfoList)
{
try
{
if (userInfo.UserID != Common.UserID)
{
//在根目录下写入日志
LogInfo.LogMessage("准备建立" + userInfo.UserID + ":" + userInfo.IPAddress + ":" + userInfo.Port.ToString(), "P2PInfo");
//创建连接信息类
ConnectionInfo connInfo = new ConnectionInfo(userInfo.IPAddress, userInfo.Port);
//把对方客户端当成服务器对应连接
Connection newTcpConnection = TCPConnection.GetConnection(connInfo);
Common.AddUserConn(userInfo.UserID, newTcpConnection);


SetUpP2PContract contract = new SetUpP2PContract();
contract.UserID = Common.UserID;

//P2p通道打通后,发送一个消息给对方用户,以便于对方用户收到消息后,建立P2P通道
newTcpConnection.SendObject("SetupP2PMessage", contract);


//在根目录下写入日志

LogInfo.LogMessage("已经建立" + userInfo.UserID + ":" + userInfo.IPAddress + ":" + userInfo.Port.ToString(), "P2PInfo");
}

}
catch
{
}

}

}
复制代码

上面的代码中,我们把相关的P2P通道建立消息写入程序文件夹下“P2PINFO.txt文件”,以便于观察P2P消息通道的建立。和通过P2P通道发送消息

(5):通过P2P通道发送消息

客户端发送消息时,查看是否与对方存在 P2P通道,如果存在通过P2P连接发送消息,否则通过服务器发送

举例说明,发送聊天消息时,先查看是否有 p2p 通道
private void chatControl1_BeginToSend(string content)
{
this.chatControl1.ShowMessage(Common.UserName, DateTime.Now, content, true);

//从客户端 Common中获取相应P2P通道
Connection p2pConnection = Common.GetUserConn(this.friendID);

if (p2pConnection != null)
{
ChatContract chatContract = new ChatContract();
chatContract.UserID = Common.UserID;
chatContract.UserName = Common.UserName;
chatContract.DestUserID = this.friendID;
chatContract.DestUserName = this.friendID;
chatContract.Content = content;
chatContract.SendTime = DateTime.Now;
p2pConnection.SendObject("ClientChatMessage", chatContract);
this.chatControl1.Focus();

LogInfo.LogMessage("通过p2p通道发送消息,当前用户ID为"+Common.UserID, "P2PINFO");


}
else
{
ChatContract chatContract = new ChatContract();
chatContract.UserID = Common.UserID;
chatContract.UserName = Common.UserName;
chatContract.DestUserID = this.friendID;
chatContract.DestUserName = this.friendID;
chatContract.Content = content;
chatContract.SendTime = DateTime.Now;
Common.TcpConn.SendObject("ChatMessage", chatContract);
this.chatControl1.Focus();

LogInfo.LogMessage("服务器转发消息", "P2PINFO");
}

}

(6)P2P通道的注销

当某个客户端掉线后,我们要把其从其他相应客户端的P2P通道注销掉。

方法:

服务器通过心跳检测,知道某连接掉线后,发送消息给其他所有客户端。
private void UserStateNotify(string userID, bool onLine)
{
try
{
//用户状态契约类
UserStateContract userState = new UserStateContract();
userState.UserID = userID;
userState.OnLine = onLine;


IList<ShortGuid> allUserID;

lock (syncLocker)
{
//获取所有用户字典中的用户ID
allUserID = new List<ShortGuid>(userManager.Values);
}

//给所有用户发送某用户的在线状态
foreach (ShortGuid netID in allUserID)
{
List<Connection> result = NetworkComms.GetExistingConnection(netID, ConnectionType.TCP);

if (result.Count > 0 && result[0].ConnectionInfo.NetworkIdentifier == netID)
{
result[0].SendObject("UserStateNotify", userState);
}
}
}
catch (Exception ex)
{
LogTools.LogException(ex, "MainForm.UserStateNotify");
}
}

客户端代码:
NetworkComms.AppendGlobalIncomingPacketHandler<UserStateContract>("UserStateNotify", IncomingUserStateNotify);
复制代码
private void IncomingUserStateNotify(PacketHeader header, Connection connection, UserStateContract userStateContract)
{
if (userStateContract.OnLine)
{
lock (syncLocker)
{
//此部分,处理用户上线,与P2p通道无关
Common.GetDicUser(userStateContract.UserID).State = OnlineState.Online;
}
}
else
{
lock (syncLocker)
{
Common.GetDicUser(userStateContract.UserID).State = OnlineState.Offline;
//当某用户下线后,删除此用户相关的p2p 通道
Common.RemoveUserConn(userStateContract.UserID);
}
}
}




...全文
2520 54 打赏 收藏 举报
写回复
用AI写文章
54 条回复
切换为时间正序
请发表友善的回复…
发表回复
frss1234 2015-04-10
  • 打赏
  • 举报
回复
高手,学习受教了
Sunny5816 2015-03-12
  • 打赏
  • 举报
回复
NetworkCommsV2.3.1相关程序的关闭代码修改如下:
  private void 退出ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                GotoExit();
            }
            catch (Exception ex)
            {
                LogTools.LogException(ex, "退出ToolStripMenuItem_Click");
            }
        }

        private void GotoExit()
        {
            try
            {
                if (MessageBox.Show("您确认要退出吗?", "", MessageBoxButtons.OKCancel) == DialogResult.OK)
                {
                    this.toExit = true;

                    NetworkComms.Shutdown();
                   
                    this.Close();
                }
            }
            catch (Exception ex)
            {
                LogTools.LogException(ex, "GotoExit");
            }
        }
   private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                if (!this.toExit)
                {

                    if ((e.CloseReason == CloseReason.ApplicationExitCall) || (e.CloseReason == CloseReason.WindowsShutDown))
                    {
                        this.toExit = true;
 
                        Environment.Exit(Environment.ExitCode);
                    }

                    else
                    {
                        this.Visible = false;
                        e.Cancel = true;
                    }

                }
            }
            catch (Exception ex)
            {
                LogTools.LogException(ex, "MainForm_FormClosing");
            }
        }
Sunny5816 2015-03-12
  • 打赏
  • 举报
回复
测试了一下,服务器端监听进程有时不能正常关闭的情况,在neworkcomms V3中已经解决
Sunny5816 2015-03-12
  • 打赏
  • 举报
回复
看来这种P2P方案有不成熟的地方,感谢大家关注
xiaoyu_code 2015-03-11
  • 打赏
  • 举报
回复
什么破东西,进程都不能结束,感觉像病毒,差评!!
微wx笑 2015-03-08
  • 打赏
  • 举报
回复
引用 20 楼 sp1234 的回复:
[quote=引用 17 楼 piaopiao_lucky 的回复:] 这个真的是P2P吗?你说的是什么意义上的P2P呢?
P2P当然不是指这种在局域网同一个网段内的c-s访。 P2P是一种"Master-Worder-Client“结构。首先,所有的终端都要登录到Master,上报终端的状态。然后当一个局域网段的Client要跟另外一个局域网段的Client通讯的时候(即使它们相隔千山万水),Master会根据它们各自的路由情况,帮它们找到另外一个Client(并且这是前两个Client都可以直接访问的),然后把这个Client升级为Worker状态。待Worker初始化完毕,Master通知前边的两个Client,告诉它们以这个Worker为中间服务器,就可以进行对话了。Master从来不做通讯服务器的工作,Master只是负责帮助寻找Worker。 而号称“局域网同一网段内的P2P”,完全看不出跟P2P协议有关系。[/quote] 刚看了一下百度百科对p2p的解释,感觉和网络编程中的p2p根本不是一个概念。 网络编程中的p2p,通信双方之间是要跨网段的,在不同的子网当中; 不然的话还要中心服务器做什么用,大家都在一个子网当中,直接就可 以找到对方, 就是飞秋一样直接通过UDP广播告诉对方你上线了就可以了。 双方的通信即不通过网络出口的防火墙,又不经过Nat转换。 只是为了发个消息,一客户端要维护(在线人数-1)*2个Tcp连接,这不是在浪费资源嘛!
微wx笑 2015-03-08
  • 打赏
  • 举报
回复
引用 5 楼 networkcomms 的回复:
1.对等模式 P2P系统中的客户端能够同时扮演客户端和服务器的角色,使两台计算机之间能够不通过服务器直接进行信息分享(QQ中当好友在线的时候发信息时,相信此时是不需要经过服务器转发的) 摘自: http://www.cnblogs.com/zhili/archive/2012/09/14/P2P_PNPR.html
当QQ启用了“聊天记录漫游”功能,消息应该就经过服务器了。 即使不启用的情况下,也应该是通过服务器的,因为要在云端对聊天内容进行监控。
  • 打赏
  • 举报
回复
学习了,谢谢!!!
「已注销」 2015-03-03
  • 打赏
  • 举报
回复
一直在学C#,从未放弃过!
Sunny5816 2015-03-03
  • 打赏
  • 举报
回复
谢谢大家关注 有朋友希望对通信框架做性能测试 可参见这篇文章 NetworkComms通信框架性能测试
Sunny5816 2015-02-28
  • 打赏
  • 举报
回复
感谢您的关注
comcyd 2015-02-28
  • 打赏
  • 举报
回复
高手,学习受教了
戈壁上的月光 2015-02-27
  • 打赏
  • 举报
回复
支持开源,支持分享
nidexiaojiaoya 2015-02-26
  • 打赏
  • 举报
回复
谢谢楼主 分享
Sunny5816 2015-02-26
  • 打赏
  • 举报
回复
是的 没有做nat相关的那部分
拜一刀 2015-02-26
  • 打赏
  • 举报
回复
这.....其实就是个没穿透的socket通讯软件?
  • 打赏
  • 举报
回复
渃水 2015-02-25
  • 打赏
  • 举报
回复
好文收藏。。。。。
Sunny5816 2015-02-25
  • 打赏
  • 举报
回复
Sunny5816 2015-02-25
  • 打赏
  • 举报
回复
谢谢大家支持 上面的连接中V3源码已经加上
加载更多回复(22)
源码直接下载地址: https://pan.quark.cn/s/0c43ed5159df 本资源为HCDA认证培训教程,旨在面向准备参加HCDA考试的学员,或希望掌握TCP/IP基础、路由、交换技术、广域网、以太网技术基本原理以及华为通用路由平台VRP上实现的相关内容。具体涵盖的模块包括:Module 1-网络互联基础、Module 2-路由和路由协议基础与实现、Module 3-以太网交换技术基础与实现、Module 4-广域网技术基本原理与实现、Module 5-网络安全基础、Module 6-华为数通产品概述 ### 华为-NAT技术详解 #### 一、概述 NAT(Network Address Translation,网络地址转换)是一种在专用网络内部使用私有IP地址的情况下,使这些设备能够通过公有IP地址进行外部通信的技术手段。NAT技术在多种网络环境中得到广泛应用,特别是在企业网络中发挥着关键性作用。作为HCDA认证培训教程的一部分,本文档将系统性地阐述NAT的基础理论及其实现方法,并深入探讨在华为VRP平台上的具体应用场景。 #### 二、NAT处理流程 NAT处理流程主要包含两种类型:基于源IP地址和基于目的IP地址的处理模式。 ##### 1. 基于源IP地址的NAT处理 **1.1 一对一NO-PAT** 一对一NO-PAT即非端口地址转换(No-Port Address Translation),在这种模式下,一个私有IP地址对应一个公有IP地址,不涉及端口号的转换。当配置了NAT no-pat时,华为设备会为已配置的多通道协议产生的实际流量数据建立server-map表项。这种方式适用于需要固定公网IP的服务器等场景。 **1.2 多对一...
内容概要:本文基于Simulink仿真平台,复现并深入研究了一篇关于全桥LLC谐振变换器的学术论文,重点聚焦于变频控制与移相控制相结合的协同策略,旨在优化变换器在不同工作模式切换过程中的动态响应特性。通过构建高精度的全桥LLC变换器仿真模型,系统分析了传统单一控制方式下模式切换易引发的电压电流冲击问题,进而提出一种融合频率调节与移相角调节的复合控制方法。该方法通过精确协调两个控制自由度,有效平抑了切换瞬态的电气应力,显著提升了系统的运行平稳性、效率及整体稳定性。文中详尽阐述了协同控制的逻辑架构、关键参数的设计与整定原则,并通过严谨的仿真对比实验,验证了所提策略在改善模式切换平滑性方面的优越性能。; 适合人群:具备扎实的电力电子与电力传动理论基础,熟练掌握Simulink/MATLAB仿真工具,正在从事高频软开关变换器、DC-DC电源变换、新能源并网逆变器等相关领域研究的硕士、博士研究生及科研技术人员。; 使用场景及目标:①深入理解全桥LLC谐振变换器的工作原理、谐振特性及其多模式运行机制;②掌握变频与移相结合的混合控制策略的设计思想与实现方法;③将其应用于高效率、高可靠性电源系统的动态性能优化,为解决实际工程中电源启停、负载突变等工况下的稳定性问题提供理论依据和技术方案。; 阅读建议:建议读者结合所提供的Simulink模型文件进行同步操作与学习,重点关注控制器模块的搭建细节、控制逻辑的实现方式以及各关键参数的调节过程。应主动对比单一变频、单一移相与协同控制三种策略下的仿真波形,从电压、电流的瞬态响应中直观感受协同控制的优势,从而深化对先进控制理论在电力电子系统中应用价值的理解。
代码转载自:https://pan.quark.cn/s/b9146ae38508 《Linux From Scratch》(LFS)是一部详尽阐述如何从源代码开始构建Linux系统的指南性著作。书中包含了完整的流程和说明,指导读者逐步构建个性化的Linux系统。这本书不仅适合那些希望探究Linux操作系统内部运作机制的用户,同时也适合那些希望构建专业级、高度定制化系统的进阶用户。LFS的中文版本将这一过程用中文详细描述,使中文用户能够毫无障碍地遵循书中的步骤来定制Linux系统。本书的翻译工作由多位译者分别承担,他们分别是金步国、ipconfigme和bobkey,各自负责不同章节的翻译任务。本书的结构分为若干部分,各部分内容如下: 第一部分主要阐述了如何构建LFS系统。在这一部分中,首先解释了LFS的定义以及构建LFS系统的必要性。接着,列出了构建LFS系统的目标读者群体、先决条件、对宿主系统的要求,并说明了排版约定和本书的组织结构。此外,还提供了版本更新记录,帮助读者了解不同版本之间的差异。 在“准备一个新分区”章节中,讲解了如何在计算机上创建新分区并在该分区上建立文件系统,为安装LFS系统进行准备。 在“软件包和补丁”章节,LFS列出了所有需要下载的软件包和补丁,并介绍了它们的用途。 在“最后的准备工作”章节,书中指导读者设置环境变量$LFS,创建$LFS/tools目录,添加LFS用户,并设定工作环境。 紧随其后,在“构建临时编译环境”章节中,详细介绍了如何创建一个临时编译环境,这个环境是编译和安装最终的LFS系统所必需的工具链。这一部分包括了如何编译安装Binutils、GCC、Linux内核头文件、Glibc,以及调整工具链等步骤。 本书详细说...
IEEE基于高频信号注入法的PMSM零低速无位置控制永磁同步电机仿真,有参考文献内容概要:本文主要介绍了一种基于高频信号注入法的永磁同步电机(PMSM)在零低速条件下的无位置传感器控制技术,重点研究了通过高频信号注入实现转子初始位置辨识和零低速运行时的精确控制。该方法利用电机交直轴高频阻抗的差异特性,通过注入高频电压信号并检测响应电流,构建转子位置观测器,从而实现对转子位置与速度的准确估计。文中详细阐述了脉振高频电压注入法的原理、观测器设计方法及其收敛性分析,并结合Simulink仿真平台验证了所提控制策略的有效性和鲁棒性。此外,文档还列举了大量相关研究主题,涵盖电机控制、新能源系统优化、智能算法应用等多个领域,体现出该技术在现代电力电子与电机控制系统中的广泛适用性。; 适合人群:具备一定电机控制与电力电子基础知识,从事自动化、电气工程及相关领域研究的研发人员与高校研究生。; 使用场景及目标:①应用于永磁同步电机驱动系统中,解决低速及零速时因反电动势微弱而导致的传统观测器失效问题;②为无位置传感器控制策略的科研仿真与工程实现提供技术参考与代码支持;③结合Matlab/Simulink进行教学演示与算法验证,促进理论与实践结合。; 阅读建议:此资源以仿真为核心,建议读者结合提供的Simulink模型与Matlab代码进行实操演练,重点关注高频信号注入参数设计、观测器结构搭建与系统稳定性分析,同时可参照文中列出的相关研究方向拓展学习深度。

111,128

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Creator Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧