之前已经讲解了Beetle简单地构建网络通讯程序,那程序紧紧是讲述了如何发送和接收数据;这一章将更深入的使用Beetle的功能,主要包括消息制定,协议分析包括消息接管处理等常用的功能。为了更好的描述所以通过创建一个聊天室程序来体现以上功能的易用性。

在实现功能之前先想好通讯上的协议需要什么功能,总结一下有:登陆,登陆成功返回,登陆和退出通过,获取现有其他用户和发送聊天信息等。需要的基础功能已经明确那就制定消息了.

通过Beetle处理消息对象必须实现IMessage接口,主要目的是由组件更好地管理buffer,避免重复的byte[]构建开销.

    public interface IMessage
{
void Save(BufferWriter writer);
void Load(BufferReader reader);
}

注册和返回

    public class Register:MsgBase
{
public string Name;
public override void Load(Beetle.BufferReader reader)
{
base.Load(reader);
Name = reader.ReadString();
}
public override void Save(Beetle.BufferWriter writer)
{
base.Save(writer);
writer.Write(Name); } }
public class RegisterResponse : MsgBase
{ }

注册和断开通知

    public class OnRegister : MsgBase
{
public OnRegister()
{
User = new UserInfo();
}
public UserInfo User;
public override void Load(Beetle.BufferReader reader)
{
base.Load(reader);
User = reader.ReadObject<UserInfo>();
}
public override void Save(Beetle.BufferWriter writer)
{
base.Save(writer);
writer.Write(User);
}
}
public class UnRegister :OnRegister
{ }

获取其他用户

    public class ListUsers:MsgBase
{
}
public class ListUsersResponse:MsgBase
{
public ListUsersResponse()
{
Users = new List<UserInfo>();
}
public override void Load(Beetle.BufferReader reader)
{
base.Load(reader);
Users = reader.ReadObjects<UserInfo>();
}
public override void Save(Beetle.BufferWriter writer)
{
base.Save(writer);
writer.Write(Users);
}
public IList<UserInfo> Users;
}

发送了聊天信息

    public class Say:MsgBase
{
public Say()
{
User = new UserInfo();
}
public override void Load(Beetle.BufferReader reader)
{
base.Load(reader);
User = reader.ReadObject<UserInfo>();
Body = reader.ReadString();
}
public override void Save(Beetle.BufferWriter writer)
{
base.Save(writer);
writer.Write(User);
writer.Write(Body);
}
public UserInfo User;
public string Body; }

协议制订完成后就进入服务端的编写了,之前已经讲述了如何构建一个socket tcp服务这里的不重复了,主要描述一下在连接事件中如何对连接进行ChannelAdapter实例化实现自动分析协议和消息分发处理。

    static void OnConnect(object sender, ChannelEventArgs e)
{
e.Channel.ChannelError += OnError;
ChannelAdapter adapter = new ChannelAdapter(e.Channel,
new HeadSizePackage(Logic.MsgBase.GetMessage));
adapter.RegisterHandler(new Program());
e.Channel.BeginReceive();
Console.WriteLine("{0} Connected!", e.Channel.EndPoint);
}

当产生连接的时候只需要针对构建一个ChannelAdapter对象即可实现协议分析,以上代码是采用头描述大小来分析协议包,组件还提供基于自定义结束符的分包方式。HeadSizePackage提供了默认的封包方式,不过可以承继它重写相关方法实现更细的封包和解包(在后面再一一讲述)。

构建了Adapter的就要注册消息处理对象,通过RegisterHandler方法进行注册,对象必须实现

    public interface IMessageHandler
{
void ProcessMessage(ChannelAdapter adapter, MessageHandlerArgs message);
}

接下来我们来实现服务端处理的代码

        public void _Register(Beetle.ChannelAdapter adapter, Logic.Register e)
{
adapter.Channel.Name = e.Name;
Logic.RegisterResponse response = new Logic.RegisterResponse();
adapter.Send(response);
Logic.OnRegister onreg = new Logic.OnRegister();
onreg.User = new Logic.UserInfo { Name= e.Name, IP= adapter.Channel.EndPoint.ToString() };
foreach (TcpChannel channel in mServer.GetOnlines())
{
if (channel != adapter.Channel)
channel.Adapter.Send(onreg);
}
Console.WriteLine("{0} login from {1}", e.Name, adapter.Channel.EndPoint);
}
public void _Say(Beetle.ChannelAdapter adapter, Logic.Say e)
{
e.User.Name = adapter.Channel.Name;
e.User.IP = adapter.Channel.EndPoint.ToString();
foreach (TcpChannel channel in mServer.GetOnlines())
{
if (channel != adapter.Channel)
channel.Adapter.Send(e);
}
Console.WriteLine("{0} say", e.User.Name);
}
public void _List(Beetle.ChannelAdapter adapter, Logic.ListUsers e)
{
Logic.ListUsersResponse response = new Logic.ListUsersResponse();
foreach (TcpChannel channel in mServer.GetOnlines())
{
if (channel != adapter.Channel)
{
response.Users.Add(new Logic.UserInfo { Name=channel.Name,IP= channel.EndPoint.ToString() });
}
}
adapter.Send(response);
}
public void ProcessMessage(ChannelAdapter adapter, MessageHandlerArgs message)
{ }

从上面的实现估计有同学感觉奇怪,为什么ProcessMessage什么都没有做。的确这个方法可以什么都不需要做,不过它可以做很多事情所有消息都经过这个方法,通过message参数的一个属性确定是否分发到具体方法中;如果不改变那个值组件会放发到具体的消息方法中。对于方法的对应关系是根据Message的类型来确定,还有方法的定义必须是public否则无法处理.

到这里服务端的工作已经完成,代码并不复杂简单的几句就完成了。接下来就是客户端的工作,相对服务端来的客户也是一样简单。为了省时间创建连接和绑定Adapter部分就不说了,其代码和服务端基本一致。

        public void _ReceiveSay(Beetle.ChannelAdapter adapter, Logic.Say e)
{
string message = string.Format(@"\viewkind4\uc1\pard\sa200\sl276\slmult1\lang2052\f0\cf1\fs22 {0} \cf0 {2} IP:{1} \cf0\line {3}",
e.User.Name, e.User.IP, DateTime.Now, e.Body);
Invoke(new Action<string>(msg => { addSay(msg); }), message);
}
public void _OtherUnRegister(Beetle.ChannelAdapter adapter, Logic.UnRegister e)
{
Invoke(new Action<Logic.UnRegister>(o =>
{
lstUsers.Items.Remove(o.User);
}), e);
}
public void _OthreRegister(Beetle.ChannelAdapter adapter, Logic.OnRegister e)
{
Invoke(new Action<Logic.OnRegister>(o =>
{
if (!lstUsers.Items.Contains(o.User))
lstUsers.Items.Add(o.User);
}), e);
}
public void _OnLogin(Beetle.ChannelAdapter adapter, Logic.RegisterResponse e)
{
Logic.ListUsers list = new Logic.ListUsers();
adapter.Send(list);
Invoke(new Action<object>(o =>
{ toolStrip2.Enabled = false;
groupBox2.Enabled = true;
}), new object());
}
public void _OnList(Beetle.ChannelAdapter adapter, Logic.ListUsersResponse e)
{
Invoke(new Action<Logic.ListUsersResponse>(o =>
{
lstUsers.Items.Clear();
foreach (Logic.UserInfo item in o.Users)
{
lstUsers.Items.Add(item);
} }), e);
}

客户端的代码主要是接收后更新UI,下面看来这个聊天室程序的效果,为了能显示图片采用了richTextBox控件,直接发送rft格式对方接收后添加到对应的richTextBox即可.

详细可以下载代码了解。

下载代码

Beelet.ChatRoom.rar (1.16 mb)

测试服务器:109.169.59.115

使用Beetle简单构建聊天室程序的更多相关文章

  1. 利用JavaUDPSocket+多线程模拟实现一个简单的聊天室程序

    对Socket的一点个人理解:Socket原意是指插座.家家户户都有五花八门的家用电器,但它们共用统一制式的插座.这样做的好处就是将所有家用电器的通电方式统一化,不需要大费周章地在墙壁上凿洞并专门接电 ...

  2. 基于AJAX的长轮询(long-polling)方式实现简单的聊天室程序

    原理: 可以看:http://yiminghe.javaeye.com/blog/294781 AJAX 的出现使得 JavaScript 可以调用 XMLHttpRequest 对象发出 HTTP ...

  3. ASP.NET 使用application和session对象写的简单聊天室程序

    ASP.Net中有两个重要的对象,一个是application对象,一个是session对象. Application:记录应用程序参数的对象,该对象用于共享应用程序级信息. Session:记录浏览 ...

  4. 高级IO复用应用:聊天室程序

    简单的聊天室程序:客户端从标准输入输入数据后发送给服务端,服务端将用户发送来的数据转发给其它用户.这里采用IO复用poll技术.客户端采用了splice零拷贝.服务端采用了空间换时间(分配超大的用户数 ...

  5. 使用Beetle简单构建高性能Socket tcp应用

    beetle是基于c#编写的高性能Socket tcp组件,它基于SocketAsyncEventArgs的实现并提供了发送队列和接收队列的支持,可以根据情况灵活地设置1-N个发送队列和接收队列.除了 ...

  6. 简易的命令行聊天室程序(Winsock,服务器&客户端)

    代码中使用WinSock2函数库,设计并实现了简单的聊天室功能.该程序为命令行程序.对于服务器和客户端,需要: 服务器:创建监听套接字,并按本地主机绑定:主线程监听并接受来自客户端的请求,并为该客户端 ...

  7. ASP.NET Signalr 2.0 实现一个简单的聊天室

    学习了一下SignalR 2.0,http://www.asp.net/signalr 文章写的很详细,如果头疼英文,还可以机翻成中文,虽然不是很准确,大概还是容易看明白. 理论要结合实践,自己动手做 ...

  8. [SignalR]一个简单的聊天室

    原文:[SignalR]一个简单的聊天室 1.说明 开发环境:Microsoft Visual Studio 2010 以及需要安装NuGet. 2.添加SignalR所需要的类库以及脚本文件: 3. ...

  9. Android简单的聊天室开发(client与server沟通)

    请尊重他人的劳动成果.转载请注明出处:Android开发之简单的聊天室(client与server进行通信) 1. 预备知识:Tcp/IP协议与Socket TCP/IP 是Transmission ...

随机推荐

  1. c#:对两个字符串大小比较(不使用c#/java内部的比较函数),按升序排序

    题目:首先需要实现一个函数:两个字符串大小比较(不得使用c#/java系统函数)的自定义函数:之后对一个字符串数据进行按升序排序(在排序过程中使用字符串大小比较时,使用自定义的字符串大小比较函数). ...

  2. IDEA初使用:解决搜狗输入法不跟随BUG

    IDEA初使用:解决搜狗输入法不跟随BUG https://blog.csdn.net/qq_27905183/article/details/79119237 嗯,学习了: 然而,效果不明显:

  3. sql server 向mysql前移数据-单引号问题

    sql server中的数据导出来 用两个单引号表示一个单引号,这样的格式可以录入到mysql中: 但是遇到特殊的中文字符,例如顿号等,不能正确的显示两个单引号: mysql导出来的数据用反斜线和一个 ...

  4. Dubbo框架应用之(三)--Zookeeper注冊中心、管理控制台的安装及解说

    我是在linux下使用dubbo-2.3.3以上版本号的zookeeper注冊中心客户端. Zookeeper是Apache Hadoop的子项目,强度相对较好,建议生产环境使用该注冊中心. Dubb ...

  5. OpenGL ES 3.0之VertexAttributes,Vertex Arrays,and Buffer Objects(九)

    顶点数据,也称为顶点属性,指每一个顶点数据.指能被用来描述每个顶点的数据,或能被所有顶点使用的常量值.例如你想绘制一个具有颜色的立方体三角形.你指定一个恒定的值用于三角形的所有三个顶点颜色.但三角形的 ...

  6. BAT,你好!字幕组,再见!——也许要跟美剧说再见了~

    对于美剧迷来说,上周的星期六(2014 年11 月 22 日)是黑色的一天. 11 月 22 日,人人影视暂时关站,并发布公告称网站正在清理内容.虽然这不是人人影视第一次关站清理,而且人人影视还给出提 ...

  7. java报错综合

    1.java.lang.NoSuchMethodError: antlr.collections.AST.getLine()I 方法一:这是因为在struts2自带的antlr-2.7.2.jar包的 ...

  8. Android输出日志Log类

    android.util.Log常用的方法有以下5个: Log.v() Log.d() Log.i() Log.w() 以及 Log.e().根据首字母分别对应VERBOSE,DEBUG,INFO,W ...

  9. ubuntu下配置华为交换机s2016

    因为笔记本没有RS232口,只有装有ubuntu的服务器上有RS232口,于是登录ubuntu 配置连接. 这里使用minicom,当然,Ubuntu源还有个图形界面的gtkterm, cutecom ...

  10. C#或Python中正则表达式元字符含意

    一.列表 元字符 描述 \ 将下一个字符标记为一个特殊字符.或一个原义字符.或一个 向后引用.或一个八进制转义符.例如,'n' 匹配字符 "n".'\n' 匹配一个换行符.序列 ' ...