一、简介:

  21世纪是出于互联网+的时代,许多传统行业和硬件挂钩的产业也逐步转向了系统集成智能化,简单来说就是需要软硬件的结合。这时,软硬件通讯便是这里面最主要的技术点,我们需要做到的是让硬件能够听懂我们系统的指令,自定义协议便应运而生。

二、设计思路:

  1)引入SuperSocket所需要的各种项目文件

  2)新建两个WinForm添加具体功能

  3)启动项同样设置两个

  4)启动服务器监听

  5) 客户登陆

  6)服务器广播

三、代码实现

  1)引入项目文件,包含在项目中,这里包含是 右键解决方案>添加>现有项目

  2)新建两个WinForm,因为WinForm是一个有Program类的类库

    2.1)在项目中会有控件类型说明

    2.2)修改Text也就是显示给客户的名字

    2.3)修改 设计>Name 这个是为了在代码中设计功能用的名称

  3)现在我们去写服务器的启动功能

    服务器启动需要一个AppServer在体,我们定义这个载体为SocketRequestInfo,让他继承AppServer,从而调用SuperSocket的内部方法实现功能。然而定义SocketRequestInfo构造时可以传入参数来实现自定义,两个参数分别是:自定义的消息格式,以及需要一个session连接。而他的构造函数只需要将 数据,操作传给工厂(DefaultReceiveFilterFactory)来代做。

    3.1)MainForm : Form

/// <summary>
/// 启动、停止
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStartStop_Click(object sender, EventArgs e)
{
if (btnStartStop.Text.Equals("启动"))
{ SocketServer = new SuperSocketServer();
SocketServer.Setup(new RootConfig(), config);
SocketServer.NewRequestReceived += SocketServer_NewRequestReceived;
SocketServer.NewSessionConnected += SocketServer_NewSessionConnected;
SocketServer.SessionClosed += SocketServer_SessionClosed; SocketServer.Start();
btnStartStop.Text = "停止"; }
else
{
SocketServer.Stop();
SocketServer.Dispose();
btnStartStop.Text = "启动";
}
lblCurrentState.Text = SocketServer.State.ToString(); }

  类SuperSocketServer : AppServer<SocketProtocolSession, SocketRequestInfo>

    public class SuperSocketServer : AppServer<SocketProtocolSession, SocketRequestInfo>
{
public SuperSocketServer()
: base(new DefaultReceiveFilterFactory<SocketRequestFilter, SocketRequestInfo>())//使用默认的接受过滤器工厂
{ }
}

  需要类SocketProtocolSession

    public class SocketProtocolSession : AppSession<SocketProtocolSession, SocketRequestInfo>
{
/// <summary>
/// 当前用户名
/// </summary>
public string UserName { get; set; } }

  以及类SocketRequestInfo

/// <summary>
/// 请求消息
/// </summary>
public class SocketRequestInfo : IRequestInfo
{ /// <summary>
/// Key[继承自接口]
/// </summary>
public string Key { get; set; } /// <summary>
/// 命令标识
/// </summary>
public byte Flag { get; set; } /// <summary>
/// 命令字
/// </summary>
public byte Command { get; set; } /// <summary>
/// 消息长度
/// </summary>
public int Length { get; set; } /// <summary>
/// 消息内容
/// </summary>
public byte[] Data { get; set; } }

  和类SocketRequestFilter : FixedHeaderReceiveFilter<SocketRequestInfo>

public class SocketRequestFilter : FixedHeaderReceiveFilter<SocketRequestInfo>
{
/// <summary>
/// 消息头长度
/// </summary>
protected const int headerSize = 4; /// <summary>
/// 构造函数
/// </summary>
public SocketRequestFilter()
: base(headerSize)
{ } /// <summary>
/// 获取消息头
/// </summary>
/// <param name="header">头部</param>
/// <param name="offset">偏移量</param>
/// <param name="length">消息头长度</param>
/// <returns></returns>
protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length)
{
return ((int)header[offset + 2] + (int)header[offset + 3] * 256);
} /// <summary>
/// 解析消息
/// </summary>
/// <param name="header">消息头</param>
/// <param name="bodyBuffer">消息体</param>
/// <param name="offset">偏移量</param>
/// <param name="length">消息体长度</param>
/// <returns></returns>
protected override SocketRequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length)
{
SocketRequestInfo request = new SocketRequestInfo();
request.Flag = bodyBuffer[offset - headerSize + 0];
request.Key = bodyBuffer.CloneRange(offset - headerSize, 1).ToHexString();
request.Command = bodyBuffer[offset - headerSize + 1];
request.Length = ((int)bodyBuffer[offset - headerSize + 2] + 256 * (int)bodyBuffer[offset - headerSize + 3]);
request.Data = bodyBuffer.CloneRange(offset, length);
return request;
}
}

    来实现这个实例的构造。

    3.2)填充配置信息,来启动服务

 //初始化服务配置
IServerConfig config = new ServerConfig
{
Name = "SuperSocketServer",
Ip = "Any",
Port = 3000,
MaxConnectionNumber = 1024,
MaxRequestLength = 4096,
ClearIdleSession = true,
ClearIdleSessionInterval = 120,
IdleSessionTimeOut = 120,
KeepAliveInterval = 60
};

  

    3.3)定义服务器的连接功功能

/// <summary>
/// 新建连接
/// </summary>
/// <param name="session"></param>
private void SocketServer_NewSessionConnected(SocketProtocolSession session)
{
txtMessage.Text += string.Format("{0}新建连接{1}", session.RemoteEndPoint.Address.ToString(), Environment.NewLine);
}

  

    3.4)定义服务器的关闭功能

/// <summary>
/// 断开连接
/// </summary>
/// <param name="session"></param>
/// <param name="value"></param>
private void SocketServer_SessionClosed(SocketProtocolSession session, SuperSocket.SocketBase.CloseReason value)
{
txtMessage.Text += string.Format("{0}断开连接{1}", session.RemoteEndPoint.Address.ToString(), Environment.NewLine);
}

  

    3.5)定义服务器的接收信息功能

 /// <summary>
/// 收到数据
/// </summary>
/// <param name="session"></param>
/// <param name="requestInfo"></param>
private void SocketServer_NewRequestReceived(SocketProtocolSession session, SocketRequestInfo requestInfo)
{ if (requestInfo.Flag == 0xAC)//0xAC类消息
{
if (requestInfo.Command == 0x01)//用户登录
{
string userName = Encoding.UTF8.GetString(requestInfo.Data);
session.UserName = userName;
txtMessage.Text += string.Format("{0}登录{1}", userName, Environment.NewLine);
session.Send(ack, 0, ack.Length);//回复
}
if (!string.IsNullOrEmpty(session.UserName))//登录后才可以收消息
{
if (requestInfo.Command == 0x02)//普通消息
{
string message = Encoding.UTF8.GetString(requestInfo.Data);
txtMessage.Text += string.Format("{0}消息:{1} {2}", session.UserName, message, Environment.NewLine);
session.Send(ack, 0, ack.Length);//回复
}
else if (requestInfo.Command == 0x03)
{ }
}
}
}

  

    3.6)启动服务

  4)客户端连接

    4.1)同样的想要玩转客户端也需要一个这样的实例,而这个实例比较简单,只需要继承类 EasyClient就好了。

    4.2)在初始化这个实例的时候需要做的连接,关闭,错误返回,接收信息的操作如下代码

private void client_Error(object sender, ErrorEventArgs e)
{
txtMessage.Text += string.Format("连接错误{0}", Environment.NewLine);
} private void client_Closed(object sender, EventArgs e)
{
txtMessage.Text += string.Format("连接断开{0}", Environment.NewLine);
btnConnect.Text = "连接";
} /// <summary>
/// 发送注册包
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void client_Connected(object sender, EventArgs e)
{
txtMessage.Text += string.Format("连接成功{0}", Environment.NewLine);
btnConnect.Text = "断开";
} /// <summary>
/// 收到消息
/// </summary>
private void OnMessageReceive(PackageInfo package)
{
if (package.Flag == 0xCA)
{
if (package.Command == 0xFE)//服务端默认应答
{
txtMessage.Text += string.Format("服务端消息接受成功{0}", Environment.NewLine);
}
else if (package.Command == 0xFA)//服务器时间
{
long ticks = BitConverter.ToInt64(package.Data, 0);
DateTime datetime = new DateTime(ticks);
txtMessage.Text += string.Format("服务器时间:{0:yyyy-MM-dd HH:mm:ss}{1}", datetime, Environment.NewLine);
}
}
}

  以上部分实现了客户端数据向Server发送时的不带数据服务器接收,注入服务器所需要的业务逻辑,然后服务端发送数据给客户端则是以lambda表达式来接收。

  5)客户端登陆:

    5.1)登陆需要做的只是实例一个上边的继承了EasyClient的客户端实例

    5.2)使用该实例send到客户端,send的数据为字符数组

    5.3)哭护短接收信息以有可以直接对数据进行操作

        private void btnLogin_Click_1(object sender, EventArgs e)
{
if (client.IsConnected)
{
List<byte> buffer = new List<byte>();
byte[] data = Encoding.UTF8.GetBytes(txtUserName.Text); buffer.Add(0xAC);
buffer.Add(0x01);
buffer.AddRange(BitConverter.GetBytes(data.Length).Take(2));
buffer.AddRange(data); //数组小抽屉,也就是把数组放在抽屉里
ArraySegment<byte> segment = new ArraySegment<byte>(buffer.ToArray());
client.Send(segment);
}
else
{
MessageBox.Show("请先连接服务器", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

  

  6)客户端发送消息

    发送消息如上登陆

/// <summary>
/// 发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param> private void btnSend_Click_1(object sender, EventArgs e)
{
if (client.IsConnected)
{
List<byte> buffer = new List<byte>();
byte[] data = Encoding.UTF8.GetBytes(txtMsgContent.Text); buffer.Add(0xAC);
buffer.Add(0x02);
buffer.AddRange(BitConverter.GetBytes(data.Length).Take(2));
buffer.AddRange(data); ArraySegment<byte> segment = new ArraySegment<byte>(buffer.ToArray());
client.Send(segment);
}
else
{
MessageBox.Show("请先连接服务器", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

  

  7)服务端广播信息

    广播消息如上登陆一样发送数组

/// <summary>
/// 广播[将当前系统时间发送到所有在线客户端]
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnBroadcast_Click(object sender, EventArgs e)
{
var sessions = SocketServer.GetAllSessions(); foreach (SocketProtocolSession session in sessions)
{
byte[] data = BitConverter.GetBytes(DateTime.Now.Ticks); List<byte> buffer = new List<byte>();
buffer.Add(0xCA);
buffer.Add(0xFA);
buffer.AddRange(BitConverter.GetBytes(data.Length).Take(2));
buffer.AddRange(data);
session.Send(buffer.ToArray(), 0, buffer.Count);
}
}

  

  

三、总结

  1)使用superSocket就需要一个YourAppServer实例,这个实例必须继承字人家SuperSocket的AppServer才能调用。想要继承这个App Server就得能拿得到会话连接和会话数据并对数据进行一系列的操作。

  2)在客户端和服务端中都有连接的代码块,在服务端这么做侧重业务的实现,而客户端只需要一个谅解功能就可以了。

   3)在发送时上诉实例使用了两种方法,一种直接发送,第二种使用数组抽屉盒子,后者更优化是第二种可以解决一个问题就是出现断网时候可以自己缓存消息,接通以后继续传输而前者不行。

  5)增加项目时候要注意引入以后需要编译

   4)源码分享:http://pan.baidu.com/s/1kVqWdNp

  

使用SuperSocket实现自定义协议C/S设计的更多相关文章

  1. SuperSocket使用自定义协议与服务端通信发送实例

    AsyncTcpSession tcpSession = new AsyncTcpSession(); public Encoding encoding = Encoding.GetEncoding( ...

  2. SuperSocket使用 IRequestInfo 和 IReceiveFilter 等对象实现自定义协议

    为什么你要使用自定义协议? 通信协议用于将接收到的二进制数据转化成您的应用程序可以理解的请求. SuperSocket提供了一个内置的通信协议“命令行协议”定义每个请求都必须以回车换行"\r ...

  3. 轨迹系列6——车载GPS对接方案汇总小结(809、自定义协议、前置库、WS)

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 最近在不同项目中对接了多个车载GPS厂商服务终端,绝大多数厂商 ...

  4. netty源码解解析(4.0)-20 ChannelHandler: 自己实现一个自定义协议的服务器和客户端

    本章不会直接分析Netty源码,而是通过使用Netty的能力实现一个自定义协议的服务器和客户端.通过这样的实践,可以更深刻地理解Netty的相关代码,同时可以了解,在设计实现自定义协议的过程中需要解决 ...

  5. Socket上自定义协议总结

    TCP只是一个可靠传输的通信管道,上层协议要你自己定的,通俗来说就是发送方和接收方的约定 自定义协议的核心有两个:1. 控制码2. 流程控制 用Socket进行通信,发送的数据包一定是有结构的,类似于 ...

  6. 从零开始实现简单 RPC 框架 7:网络通信之自定义协议(粘包拆包、编解码)

    当 RPC 框架使用 Netty 通信时,实际上是将数据转化成 ByteBuf 的方式进行传输. 那如何转化呢?可不可以把 请求参数 或者 响应结果 直接无脑序列化成 byte 数组发出去? 答:直接 ...

  7. 利用Netty构建自定义协议的通信

    在复杂的网络世界中,各种应用之间通信需要依赖各种各样的协议,比如:HTTP,Telnet,FTP,SMTP等等. 在开发过程中,有时候我们需要构建一些适应自己业务的应用层协议,Netty作为一个非常优 ...

  8. C#综合揭秘——通过修改注册表建立Windows自定义协议

    引言 本文主要介绍注册表的概念与其相关根项的功能,以及浏览器如何通过连接调用自定义协议并与客户端进行数据通信.文中讲及如何通过C#程序.手动修改.安装项目等不同方式对注册表进行修改.其中通过安装项目对 ...

  9. 【Win10 UWP】URI Scheme(二):自定义协议的处理和适用场景

    上一篇提到Windows Store协议的使用,其实Windows Store协议仅是系统内建的一种协议规则.我们也可以自己定义一套规范的URI-Scheme,除了可以给其他App调用外,本应用也可以 ...

随机推荐

  1. 对抗攻击(一) FGSM

    引言 在对抗样本综述(二)中,我们知道了几种著名的对抗攻击和对抗防御的方法.下面具体来看下几种对抗攻击是如何工作的.这篇文章介绍FGSM(Fast Gradient Sign Method). 预备知 ...

  2. __schedule的一些小细节

    (代码主要参考5.10) 1. __schedule的参数preempt static void __sched notrace __schedule(bool preempt) preempt是一个 ...

  3. final修饰符(2)

    final局部变量 系统不会对局部变量进行初始化,局部变量必须又程序员显示初始化,因此使用final修饰局部变量,可以在声明时指定默认值,也可以在后面的代码中对该final变量赋初始值,但只能赋值一次 ...

  4. noip模拟23[联·赛·题]

    \(noip模拟23\;solutions\) 怎么说呢??这个考试考得是非常的惨烈,一共拿了70分,为啥呢 因为我第一题和第三题爆零了,然后第二题拿到了70分,还是贪心的分数 第一题和第二题我调了好 ...

  5. 网络损伤仪WANsim中的时延的不同模型

    网络损伤仪WANsim中的3种时延模型 时延指的是报文从网络的一端到达另一端所花费的时间. 网络损伤仪WANsim中为用户提供了3种时延损伤的模型.常量模型.均匀分布.正态分布. 这3种模型按照各自的 ...

  6. 浅析java中的IO流

    在java中IO类很庞大,初学的时候觉得傻傻分不清楚.其实java流归根结底的原理是普通字节流,字节缓冲流,转换流.最基础的是普通字节流,即从硬盘读取字节写入到内存中,但在实际使用中又发现一些特殊的需 ...

  7. Java后端编译

    概述 如果我们把字节码看作是程序语言的一种中间表示形式(Intermediate Representation,IR)的话, 那编译器无论在何时.在何种状态下把Class文件转换成与本地基础设施(硬件 ...

  8. tkinter 基础教程

    目录 介绍 模块 导入方式 API 使用 主窗口 运行窗口 组件列表介绍 Label 标签 Button 按钮 Options 属性选项 文本框 Entry 单行文本框 Text 多行文本框 文本框属 ...

  9. spring中的组合模式

    org.springframework.cache.support.CompositeCacheManager /* * Copyright 2002-2016 the original author ...

  10. 10分钟了解微服务、容器和Kubernetes

    什么是微服务? 什么是微服务?你应该使用微服务吗?微服务与容器和 Kubernetes 有什么关系?如果这些问题在您的日常生活中不断出现,那么这篇文章适合您. 从根本上说,微服务只是一个运行在服务器或 ...