我知道这样的文章在博客园已经多的大家都不想看了,但是这是我的系列文章开始,请各位大神见谅了。

多线程,线程执行器,(详见),socket通信相关 (详见

本人blog相关文章测试代码,示例,完整版svn地址。(http://code.taobao.org/svn/flynetwork_csharp/trunk/Flynetwork/BlogTest)

提供全部源码功能块。希望各位大神,提供宝贵意见。

莫倩,完成了多线程辅助类库完整功能(或许后期会有bug需要修复或者优化),socket完成了tcp和http服务监听功能,udp和websocket还在完善的状态中。

如果有通信愿意和我一起完善这个辅助类库,请联系我,开通svn授权。

所以源码免费提供使用,欢迎各位爱好者,加入到项目中,无论是个人,企业,商用,都不限制。唯一要求请保留以下字样。谢谢合作~!

 /**
  *
  * @author 失足程序员
  * @Blog http://www.cnblogs.com/ty408/
  * @mail 492794628@qq.com
  * @phone 13882122019
  *
  */

好了开始我们的话题

在服务器项目开发中,最总要的也就是登陆问题了或者说叫授权问题。

我们先创建一个console的程序

引用我的两个库 Sz.Network.SocketPool ,Sz.Network.ThreadPool 分别是socket 帮助库线程帮助库。

Sz表示失足的意思。请见谅。偶喜欢上这个代号了“失足程序员”

我们先创建一个消息处理器 MessagePool

  public class MessagePool : ISocketPool
     {
         public void ActiveSocket(IOSession client)
         {
         }

         public void CloseSocket(IOSession client)
         {

         }

         public void ReadMessage(IOSession client, SocketMessage message)
         {

         }

         public void ActiveHttp(HttpClient client, string bind, Dictionary<string, string> parms)
         {
             if (bind.Equals("/test/"))
             {
                 ThreadManager.GetInstance.AddTask(ServerManager.LoginThreadID, new LoginHttpHandler(client, parms));
             }
         }

         public void IOSessionException(IOSession client, Exception exception)
         {
             Logger.Error("内部错误", exception);
         }

         public void HttpException(HttpClient client, Exception exception)
         {
             Logger.Error("内部错误", exception);
         }
     }

然后在mian函数里面加入

  Sz.Network.SocketPool.ListenersBox.GetInstance.SetParams(new MessagePool(), typeof(MarshalEndian));
             Sz.Network.SocketPool.ListenersBox.GetInstance.Start("tcp:*:9527", "http://*:8001/test/");

这样我们就开启了服务器的监听,这里简单介绍一下为什么我创建了tcp和http的两个监听呢?

这是因为经验和工作关系,因为我是致力于游戏开发的,服务器端需要创建两个tcp通常是用于正常通信的,而http监听的登陆模块,或者一些非总要性数据交换以及第三方登陆授权需要开启的。同样还因为http是短连接,无需保存通信对象,减少了系统消耗,和tcp数量级消耗。

如果你不理解可以不加入http的监听的。直接看tcp的socket。

 [-- ::::Info ] Start Listen Tcp Socket ->
 [-- ::::Info ] Start Listen Http Socket -> /test/

运行程序,输出。

开启了tcp和http的监听,http我们今天暂时忽略其作用吧。

我们开始准备登陆模块的开发,同学都知道登陆首先要面临的就是多点同时登陆问题。

如果加入 lock 来防止多点同时登陆,那么势必照成服务器卡顿。吞吐量不高等因素。那么我们考虑把这一块加入到单独的线程惊喜处理。也就是帮登陆和登出,放到同一个线程处理。不用加锁,也能做到防止多点同时登陆。

接下来我们需要从 Sz.Network.ThreadPool 库中取出一个线程

public static readonly long LoginThreadID = ThreadManager.GetInstance.GetThreadModel("登陆处理器");

用于控制登陆和登出

创建一个 CloseTcpHandler 处理链接端口的处理程序 需要继承 Sz.Network.ThreadPool 下面的 TaskBase

  public class CloseTcpHandler : TaskBase
     {

         IOSession client;
         SocketMessage message;

         public CloseTcpHandler(IOSession client)
             : base("Tcp登陆处理")
         {
             this.client = client;
         }

         public override void TaskRun()
         {

         }
     }

那么我们修改一下 MessagePool 类

   public void ActiveSocket(IOSession client)
        {
            //client.SendMsg(new SocketMessage(1, System.Text.UTF8Encoding.Default.GetBytes("Holle Server!client")));
        }

        public void CloseSocket(IOSession client)
        {
            ThreadManager.GetInstance.AddTask(ServerManager.LoginThreadID, new CloseTcpHandler(client));
        }

这样断开链接处理就交到了 ServerManager.LoginThreadID 这个线程里面处理了

我们再来创建一下 LoginTcpHandler 处理登陆程序 需要继承 Sz.Network.ThreadPool 下面的 TaskBase

修改  MessagePool 类 处理登陆消息

 public void ReadMessage(IOSession client, SocketMessage message)
         {
             switch (message.MsgID)
             {
                 ://登陆
                 :
                     ThreadManager.GetInstance.AddTask(ServerManager.LoginThreadID, new LoginTcpHandler(client, message));
                     break;
                 default:
                     Logger.Error("未绑定消息ID " + message.MsgID);
                     break;
             }
         }

这里是我自定义消息ID是1和2的一个是登陆一个是登出。

这样就把登陆的事件也交给了ServerManager.LoginThreadID 这个线程里面处理了

LoginTcpHandler taskrun方法

  public override void TaskRun()
         {
             using (MemoryStream msReader = new MemoryStream(message.MsgBuffer))
             {
                 using (System.IO.BinaryReader srReader = new BinaryReader(msReader, UTF8Encoding.Default))
                 {
                     using (MemoryStream msWriter = new MemoryStream())
                     {
                         using (System.IO.BinaryWriter srWriter = new BinaryWriter(msWriter, UTF8Encoding.Default))
                         {
                             switch (message.MsgID)
                             {
                                 ://登陆
                                     string username = srReader.ReadString();
                                     if (!LoginManager.GetInstance.LoginNames.Contains(username))
                                     {
                                         LoginManager.GetInstance.LoginNames.Add(username);
                                         if (!LoginManager.GetInstance.LoginIPs.ContainsKey(client.ID))
                                         {
                                             LoginManager.GetInstance.LoginIPs[client.ID] = username;
                                             LoginManager.GetInstance.Sessions.Add(client);
                                         }
                                         srWriter.Write(true);
                                         srWriter.Write(username + " 登陆聊天室");
                                         Logger.Info(client.RemoteEndPoint + " " + username + " 登陆成功");
                                         SocketMessage sm = new SocketMessage(message.MsgID, msWriter.GetBuffer());
                                         ServerManager.GetInstance.Tell_All(sm);
                                     }
                                     else
                                     {
                                         srWriter.Write(false);
                                         srWriter.Write("登录名称重复,请换一个");
                                         Logger.Info(client.RemoteEndPoint + " " + username + " 登录名称重复!");
                                         SocketMessage sm = new SocketMessage(message.MsgID, msWriter.GetBuffer());
                                         client.SendMsg(sm);
                                     }
                                     break;
                                 :// 退出登陆

                                     break;
                                 default:

                                     break;
                             }
                         }
                     }
                 }
             }
         }
CloseTcpHandler taskrun方法

 public override void TaskRun()
         {
             if (LoginManager.GetInstance.LoginIPs.ContainsKey(client.ID))
             {
                 string username = LoginManager.GetInstance.LoginIPs[client.ID];
                 LoginManager.GetInstance.LoginIPs.Remove(client.ID);
                 LoginManager.GetInstance.LoginIPs.Remove(username);
                 LoginManager.GetInstance.Sessions.Remove(client);
                 using (MemoryStream msWriter = new MemoryStream())
                 {
                     using (System.IO.BinaryWriter srWriter = new BinaryWriter(msWriter, UTF8Encoding.Default))
                     {
                         srWriter.Write(username + "退出聊天室");
                         SocketMessage sm = , msWriter.GetBuffer());//3表示发送消息
                         ServerManager.GetInstance.Tell_All(sm);
                     }
                 }
             }
         }

这里由于代码没有贴全,有兴趣的可以下载源码试试

启动两个客户端后,看到创建了两个链接,并且登陆到服务器。

由于聊天和登陆所以不同的两个模块,为了提高效率 我们再次创建一个聊天线程

public static readonly long ChatThreadID = ThreadManager.GetInstance.GetThreadModel("聊天处理器");

接下来我们在 MessagePool 的 ReadMessage 方法的switch里面加入

://聊天
                    ThreadManager.GetInstance.AddTask(ServerManager.ChatThreadID, new Chat.ChatHandler(client, message));
                    break;

这样我们就把所有的聊天消息转发的ServerManager.ChatThreadID这个线程处理。就是卡顿情况,也不会影响客户端聊天发送消息和新客户端请求登陆。

这里我们还可以考虑分组,聊天分组功能。比如私聊,群聊等分组线程进行执行。

创建一个 ChatHandler  需要继承 Sz.Network.ThreadPool 下面的 TaskBase

 public class ChatHandler : TaskBase
     {
         IOSession client;

         SocketMessage message;

         public ChatHandler(IOSession client, SocketMessage message)
             : base("聊天处理任务")
         {
             this.client = client;
             this.message = message;
         }

         public override void TaskRun()
         {
             using (MemoryStream msWriter = new MemoryStream())
             {
                 using (System.IO.BinaryWriter srWriter = new BinaryWriter(msWriter, UTF8Encoding.Default))
                 {
                     //构建输入buffer
                     //验证登陆情况
                     if (LoginManager.GetInstance.LoginIPs.ContainsKey(client.ID))
                     {
                         string username = LoginManager.GetInstance.LoginIPs[client.ID];
                         using (MemoryStream msReader = new MemoryStream(message.MsgBuffer))
                         {
                             using (System.IO.BinaryReader srReader = new BinaryReader(msReader, UTF8Encoding.Default))
                             {
                                 string msg = srReader.ReadString();
                                 msg = client.RemoteEndPoint + " " + username + " " + msg;
                                 Logger.Info(msg);
                                 srWriter.Write(msg);
                                 SocketMessage sm = new SocketMessage(message.MsgID, msWriter.GetBuffer());
                                 ServerManager.GetInstance.Tell_All(sm);
                             }
                         }
                     }
                     else
                     {
                         srWriter.Write("尚未登陆");
                         SocketMessage sm = new SocketMessage(message.MsgID, msWriter.GetBuffer());
                         client.SendMsg(sm);
                     }
                 }
             }
         }
     }

发个消息试试

这里一个简单的聊天服务器,登陆到聊天就算完成了,客户端是wpf的程序,没有贴出源码和过程,有需要或者要研究的亲请下载svn源码,自行查看情况。

一步一步开发Game服务器(二)完成登陆,聊天的更多相关文章

  1. 一步一步开发Game服务器(三)加载脚本和服务器热更新(二)完整版

    上一篇文章我介绍了如果动态加载dll文件来更新程序 一步一步开发Game服务器(三)加载脚本和服务器热更新 可是在使用过程中,也许有很多会发现,动态加载dll其实不方便,应为需要预先编译代码为dll文 ...

  2. 一步一步开发Game服务器(二)登陆2

    上一篇文章,讲解了简单的登陆情况.接下来我们继续讲解登陆模块. 在正常的游戏服务器情况下.在尚未登录前可以查看服务器大区情况,登陆后也可以查看服务器大区情况,然后选择大区服务器.进行登录操作. 这样的 ...

  3. 一步一步开发Game服务器(四)地图线程

    时隔这么久 才再一次的回归正题继续讲解游戏服务器开发. 开始讲解前有一个问题需要修正.之前讲的线程和定时器线程的时候是分开的. 但是真正地图线程与之前的线程模型是有区别的. 为什么会有区别呢?一个地图 ...

  4. 一步一步跟我学DeviceOne开发 - 仿微信应用(一,二,三)

    这是一个系列的文档,长期目标是利用DeviceOne开发一些目前使用广泛的优质手机应用,我们会最大化的实现这些应用的每一个功能和细节,不只停留在简单的UI模仿和Demo阶段,而是一个基本可以使用的实际 ...

  5. 一步一步开发Game服务器(一)

    什么是服务器?对于很多人来说也许只是简单成为在服务器端运行的程序的确如此,服务器通常意义就是说在服务器端运行的程序而已.那么我们怎么理解和分析游戏服务器哪? 传统意义上来说,程序运行后,正常流程, 启 ...

  6. JAVA+PHP+阿里云组件纯手工实现POP、SMTP、IMAP开发邮件服务器(二)

    java开发邮件服务器的接收模块 用java建立socket服务端,监听端口25,实现SMTP协议.即可完成邮件服务器的接收模块. 这里要注意的是,SMTP协议其实可以分为两种.一种是你用手机.PC等 ...

  7. 跟我一步一步开发自己的Openfire插件

    http://www.blogjava.net/hoojo/archive/2013/03/07/396146.html 跟我一步一步开发自己的Openfire插件 这篇是简单插件开发,下篇聊天记录插 ...

  8. xmppmini 项目详解:一步一步从原理跟我学实用 xmpp 技术开发 2.登录的实现

    第二章登录的实现 金庸<倚天屠龙记> 张三丰缓缓摇头,说道:“少林派累积千年,方得达成这等绝技,决非一蹴而至,就算是绝顶聪明之人,也无法自创.”他顿了一顿,又道:“我当年在少林寺中住过,只 ...

  9. 一步一步实现HTTP服务器-开篇

    缘起 翻开清单,一条条计划一直列在那里,一天又一天,不知道什么时候写下了它,也知不道什么时候完成它,它一直在那静静的等待着. 静下心来,反思自己,才发现自己是多么的无知,多么的没有毅力.设定了无数目标 ...

随机推荐

  1. 一张图解释SQL Server集群、镜像、复制、日志传送

    一张图解释SQL Server集群.镜像.复制.日志传送 本文版权归作者所有,未经作者同意不得转载.

  2. 阿里聚安全受邀参加SFDC安全大会,分享互联网业务面临问题和安全创新实践

    现今,技术引领的商业变革已无缝渗透入我们的日常生活,「技术改变生活」的开发者们被推向了创新浪潮的顶端.国内知名的开发者技术社区 SegmentFault 至今已有四年多了,自技术问答开始,他们已经发展 ...

  3. 在Linux上编译dotnet cli的源代码生成.NET Core SDK的安装包

    .NET 的开源,有了更多的DIY乐趣.这篇博文记录一下在新安装的 Linux Ubuntu 14.04 上通过自己动手编译 dotnet cli 的源代码生成 .net core sdk 的 deb ...

  4. ABP理论学习之工作单元(Unit of Work)

    返回总目录 本篇目录 公共连接和事务管理方法 ABP中的连接和事务管理 仓储类 应用服务 工作单元 工作单元详解 关闭工作单元 非事务的工作单元 工作单元方法调用其它 工作单元作用域 自动保存 IRe ...

  5. Python黑帽编程1.1虚拟机安装和配置 Kali Linux 2016

    Python黑帽编程1.1虚拟机安装和配置 Kali Linux 2016 0.1  本系列教程说明 本系列教程,采用的大纲母本为<Understanding Network Hacks Att ...

  6. 细嗅Promise

    读完这篇文章,预计会消耗你 40 分钟的时间. Ajax 出现的时候,刮来了一阵异步之风,现在 Nodejs 火爆,又一阵异步狂风刮了过来.需求是越来越苛刻,用户对性能的要求也是越来越高,随之而来的是 ...

  7. WPF PlacementTarget技巧

    <Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winf ...

  8. Redis 哨兵模式实现主从故障互切换

    200 ? "200px" : this.width)!important;} --> 介绍 Redis Sentinel 是一个分布式系统, 你可以在一个架构中运行多个 S ...

  9. FTP文件服务搭建与同步传输

    需求       搭建一台FTP服务器,用于文件的上传与下载:同时将FTP服务器目录中的文件同步到多个服务器中,实现同步更新,同时文件需要控制用户访问对应的文件夹权限. 需要用到的软件有:bestsy ...

  10. java stopwatch 功能

    C#中有一个stopwatch的功能,主要是用来监测程序执行时间的.java之前一直都在用如下方式完成: public static void main(String[] args) { long s ...