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

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

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

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

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

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

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

1 /**
2  *
3  * @author 失足程序员
4  * @Blog http://www.cnblogs.com/ty408/
5  * @mail 492794628@qq.com
6  * @phone 13882122019
7  *
8  */

好了开始我们的话题

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

我们先创建一个console的程序

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

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

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

 1  public class MessagePool : ISocketPool
 2     {
 3         public void ActiveSocket(IOSession client)
 4         {
 5         }
 6
 7         public void CloseSocket(IOSession client)
 8         {
 9
10         }
11
12         public void ReadMessage(IOSession client, SocketMessage message)
13         {
14
15         }
16
17
18         public void ActiveHttp(HttpClient client, string bind, Dictionary<string, string> parms)
19         {
20             if (bind.Equals("/test/"))
21             {
22                 ThreadManager.GetInstance.AddTask(ServerManager.LoginThreadID, new LoginHttpHandler(client, parms));
23             }
24         }
25
26         public void IOSessionException(IOSession client, Exception exception)
27         {
28             Logger.Error("内部错误", exception);
29         }
30
31         public void HttpException(HttpClient client, Exception exception)
32         {
33             Logger.Error("内部错误", exception);
34         }
35     }

View Code

然后在mian函数里面加入

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

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

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

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

1 [2015-04-15 18:12:09:899:Info ] Start Listen Tcp Socket -> 0.0.0.0:9527
2 [2015-04-15 18:12:09:906:Info ] Start Listen Http Socket -> 0.0.0.0:8001/test/

运行程序,输出。

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

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

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

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

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

用于控制登陆和登出

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

 1  public class CloseTcpHandler : TaskBase
 2     {
 3
 4         IOSession client;
 5         SocketMessage message;
 6
 7         public CloseTcpHandler(IOSession client)
 8             : base("Tcp登陆处理")
 9         {
10             this.client = client;
11         }
12
13
14         public override void TaskRun()
15         {
16
17         }
18     }

View Code

那么我们修改一下 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

View Code

修改  MessagePool 类 处理登陆消息

 1 public void ReadMessage(IOSession client, SocketMessage message)
 2         {
 3             switch (message.MsgID)
 4             {
 5                 case 1://登陆
 6                 case 2:
 7                     ThreadManager.GetInstance.AddTask(ServerManager.LoginThreadID, new LoginTcpHandler(client, message));
 8                     break;
 9                 default:
10                     Logger.Error("未绑定消息ID " + message.MsgID);
11                     break;
12             }
13         }

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

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

LoginTcpHandler taskrun方法

 1  public override void TaskRun()
 2         {
 3             using (MemoryStream msReader = new MemoryStream(message.MsgBuffer))
 4             {
 5                 using (System.IO.BinaryReader srReader = new BinaryReader(msReader, UTF8Encoding.Default))
 6                 {
 7                     using (MemoryStream msWriter = new MemoryStream())
 8                     {
 9                         using (System.IO.BinaryWriter srWriter = new BinaryWriter(msWriter, UTF8Encoding.Default))
10                         {
11                             switch (message.MsgID)
12                             {
13                                 case 1://登陆
14                                     string username = srReader.ReadString();
15                                     if (!LoginManager.GetInstance.LoginNames.Contains(username))
16                                     {
17                                         LoginManager.GetInstance.LoginNames.Add(username);
18                                         if (!LoginManager.GetInstance.LoginIPs.ContainsKey(client.ID))
19                                         {
20                                             LoginManager.GetInstance.LoginIPs[client.ID] = username;
21                                             LoginManager.GetInstance.Sessions.Add(client);
22                                         }
23                                         srWriter.Write(true);
24                                         srWriter.Write(username + " 登陆聊天室");
25                                         Logger.Info(client.RemoteEndPoint + " " + username + " 登陆成功");
26                                         SocketMessage sm = new SocketMessage(message.MsgID, msWriter.GetBuffer());
27                                         ServerManager.GetInstance.Tell_All(sm);
28                                     }
29                                     else
30                                     {
31                                         srWriter.Write(false);
32                                         srWriter.Write("登录名称重复,请换一个");
33                                         Logger.Info(client.RemoteEndPoint + " " + username + " 登录名称重复!");
34                                         SocketMessage sm = new SocketMessage(message.MsgID, msWriter.GetBuffer());
35                                         client.SendMsg(sm);
36                                     }
37                                     break;
38                                 case 2:// 退出登陆
39
40                                     break;
41                                 default:
42
43                                     break;
44                             }
45                         }
46                     }
47                 }
48             }
49         }

View Code

CloseTcpHandler taskrun方法

 1 public override void TaskRun()
 2         {
 3             if (LoginManager.GetInstance.LoginIPs.ContainsKey(client.ID))
 4             {
 5                 string username = LoginManager.GetInstance.LoginIPs[client.ID];
 6                 LoginManager.GetInstance.LoginIPs.Remove(client.ID);
 7                 LoginManager.GetInstance.LoginIPs.Remove(username);
 8                 LoginManager.GetInstance.Sessions.Remove(client);
 9                 using (MemoryStream msWriter = new MemoryStream())
10                 {
11                     using (System.IO.BinaryWriter srWriter = new BinaryWriter(msWriter, UTF8Encoding.Default))
12                     {
13                         srWriter.Write(username + "退出聊天室");
14                         SocketMessage sm = new SocketMessage(3, msWriter.GetBuffer());//3表示发送消息
15                         ServerManager.GetInstance.Tell_All(sm);
16                     }
17                 }
18             }
19         }

View Code

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

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

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

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

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

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

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

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

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

 1 public class ChatHandler : TaskBase
 2     {
 3         IOSession client;
 4
 5         SocketMessage message;
 6
 7
 8         public ChatHandler(IOSession client, SocketMessage message)
 9             : base("聊天处理任务")
10         {
11             this.client = client;
12             this.message = message;
13         }
14
15
16         public override void TaskRun()
17         {
18             using (MemoryStream msWriter = new MemoryStream())
19             {
20                 using (System.IO.BinaryWriter srWriter = new BinaryWriter(msWriter, UTF8Encoding.Default))
21                 {
22                     //构建输入buffer
23                     //验证登陆情况
24                     if (LoginManager.GetInstance.LoginIPs.ContainsKey(client.ID))
25                     {
26                         string username = LoginManager.GetInstance.LoginIPs[client.ID];
27                         using (MemoryStream msReader = new MemoryStream(message.MsgBuffer))
28                         {
29                             using (System.IO.BinaryReader srReader = new BinaryReader(msReader, UTF8Encoding.Default))
30                             {
31                                 string msg = srReader.ReadString();
32                                 msg = client.RemoteEndPoint + " " + username + " " + msg;
33                                 Logger.Info(msg);
34                                 srWriter.Write(msg);
35                                 SocketMessage sm = new SocketMessage(message.MsgID, msWriter.GetBuffer());
36                                 ServerManager.GetInstance.Tell_All(sm);
37                             }
38                         }
39                     }
40                     else
41                     {
42                         srWriter.Write("尚未登陆");
43                         SocketMessage sm = new SocketMessage(message.MsgID, msWriter.GetBuffer());
44                         client.SendMsg(sm);
45                     }
46                 }
47             }
48         }
49     }

View Code

发个消息试试

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

一步一步开发Game服务器(二)完成登陆,聊天相关推荐

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

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

  2. 路由器二次开发一步一步把工业路由器变成一个高端的可指定出网、节点和链路的路由器,包含详细过程及快捷脚本(五)

    路由器二次开发一步一步把工业路由器变成一个高端的可指定出网.节点和链路的路由器,包含详细过程及快捷脚本(五). 如果没有 路由器 可以采用 废旧的电脑,详细环境部署参考第(一)篇文章,这里采用800米 ...

  3. 路由器二次开发一步一步把工业路由器变成一个高端的可指定出网、节点和链路的路由器,包含详细过程及快捷脚本(四)

    路由器二次开发一步一步把工业路由器变成一个高端的可指定出网.节点和链路的路由器,包含详细过程及快捷脚本(四). 如果没有 路由器 可以采用 废旧的电脑,详细环境部署参考第(一)篇文章,这里采用800米 ...

  4. 路由器二次开发一步一步把工业路由器变成一个高端的可指定出网、节点和链路的路由器,包含详细过程及快捷脚本(二)

    路由器二次开发一步一步把工业路由器变成一个高端的可指定出网.节点和链路的路由器,包含详细过程及快捷脚本(二). 如果没有路由器可以采用废旧的电脑,详细环境部署参考第(一)篇文章,这里采用800米的工业 ...

  5. 群辉服务器更新系统教程,男人的生产力工具 篇二百四十六:新手玩转群晖NAS:一步一步教你更新群晖DSM系统...

    男人的生产力工具 篇二百四十六:新手玩转群晖NAS:一步一步教你更新群晖DSM系统 2020-08-05 10:46:03 18点赞 175收藏 59评论 新手与新知男人的生产力工具 篇二百三十九:新 ...

  6. 一步一步在阿里云上架一个应用系统(云服务器ECS和轻量应用服务器选型)1

    一步一步在阿里云上架一个应用系统(云服务器ECS和轻量应用服务器选型) - 1 一步一步在阿里云上架一个应用系统(云服务器ECS选购配置) - 2 文章目录 前言 步骤 云服务器 所有组件 云服务器 ...

  7. 高级性能服务器编程模型【IOCP完成端口】开发实现【二】

    因为需要参考各种资料,所以还是需要一些时间才能够做好的. 而且在开发中,还会面对一些不得不仔细去解决的问题. 我打算尽量从Win32API的基础上面进行开发,能够不使用Delphi封装的类就不用,任何 ...

  8. Java程序员从笨鸟到菜鸟之(一百零八)一步一步学习webservice(二)webservice基本原理

    本来这第二篇打算讲解"开发第一个基于XFire的webservice"的内容来着.但是想想.开发实例只是局限于了会用的层面上.如果想真正的理解webservice还是需要挖掘其原理 ...

  9. 一步一步学Spring Boot(二)课程发布了~~~

    课程名称 <一步一步学Spring Boot(二)> 学习地址 CSDN学习地址: http://edu.csdn.net/lecturer/994 51CTO学习地址:http://ed ...

最新文章

  1. k8s(1)-使用kubeadm安装Kubernetes
  2. Mac 安装laravel 框架
  3. CentOS修改yum源为阿里云
  4. java实例_图例 | Java混合模式分析之火焰图实例
  5. idea springmvc项目搭建_ssm框架整合搭建流程 - 小白小承
  6. MySQL主从复制的原理及配置方法(比较详细)
  7. 7模型集成:细粒度用户评论情感分析冠军思路及源码
  8. Linux资源监控工具
  9. OC Gen X Mac(一键制作黑苹果OpenCore EFI文件)v最新版
  10. nodejs操作sqlserver数据_SQL Server数据库损坏和修复
  11. android studio : Could not find org.jetbrains.kotlin:kotlin-stdlib-jre7:1.5.31
  12. python xlwt_python – 使用xlwt写入现有工作簿
  13. android将毫秒转换成日期,如何在Android中将毫秒转换为日期格式?
  14. Java程序员转行都可以做什么呢?
  15. 300份奖品待领取 | 你的烦恼值钱啦!华为云 DevCloud 年度开发者的烦恼有奖征集火爆开启!...
  16. Hanoi塔(汉诺塔/梵天塔)问题
  17. Bootstrap轮播插件
  18. 机器学习笔记 --- 数学符号以及读法
  19. 内存申请与释放(转)
  20. linux,unix,bsd命令收集

热门文章

  1. 瑞星发布中国用户最常用十大密码:abc123
  2. 20121223-命令与征服全面变更
  3. 连接数据库,写了一个登录注册界面
  4. Gensim库的使用——Gensim库的核心概念介绍
  5. 关于一个简单函数方程问题的深入探究
  6. 写好 JS 条件语句的 5 条守则
  7. 还在用百度查找资源?不要落后啦,5款出奇好用的资源网送给你!
  8. Oracle数据库查询十个小技巧
  9. 入门级理财书-小狗钱钱-读书笔记
  10. 幻数java题_幻数