一、简介:

  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. Linux磁盘管理与文件系统

    文章目录一.硬盘结构二.MBR与磁盘分区表示三.磁盘分区结构四.文件系统类型●1.XFS文件系统●2.SWAP,交换文件系统●3.Linux支持的其他文件系统类型五.命令部分--检测并确认新硬盘●1. ...

  2. BPDU、Hybrid、MSTP

    BPDU.Hybrid.MSTP      一.BPDU         1)BPDU概述         2)BPDU类型         3)BPDU报文字段      二.Hybrid     ...

  3. python 实时监控剪切板,并替换其中的部分内容,重新写入剪切板

    #实时监控剪贴板内容的变化,并替换其中的回车,换行,逗号,再写入剪切板,以供使用. import pyperclip import time last_string = pyperclip.paste ...

  4. 【论文阅读】DSDNet Deep Structured self-Driving Network

    前言引用 [2] DSDNet Deep Structured self-Driving Network Wenyuan Zeng, Shenlong Wang, Renjie Liao, Yun C ...

  5. File类与常用IO流第九章——转换流

    第九章.转换流 字节编码和字符集 编码:按照某种规则将字符以二进制存储到计算机中. 解码:将存储在计算机中的二进制数按照某种规则解析显示出来. 字符编码:Character Encoding ,就是一 ...

  6. Window安装构建神器Jenkins

    Jenkins是什么? Jenkins是一款开源 CI&CD 软件,用于自动化各种任务,包括构建.测试和部署软件.支持各种运行方式,可通过系统包.Docker 或者通过一个独立的 Java 程 ...

  7. 在LinuxMint 17 MATE中安装NVIDIA显卡驱动

    第一步:在Linux系统中安装Nvidia显卡驱动需要关闭X Server. 打开终端,进入ROOT权限,执行以下命令 $ sudo service mdm stop 此时将会把X Server关闭, ...

  8. Scrapy入门到放弃03:理解settings配置,监控Scrapy引擎

    前言 代码未动,配置先行.本篇文章主要讲述一下Scrapy中的配置文件settings.py的参数含义,以及如何去获取一个爬虫程序的运行性能指标. 这篇文章无聊的一匹,没有代码,都是配置化的东西,但是 ...

  9. js学习笔记之正则

    () 是为了提取匹配的字符串.表达式中有几个()就有几个相应的匹配字符串.(\s*)表示连续空格的字符串.[]是定义匹配的字符范围.比如 [a-zA-Z0-9] 表示相应位置的字符要匹配英文字符和数字 ...

  10. 机器学习 - k-means聚类

    k-means简介 k-means是无监督学习下的一种聚类算法,简单说就是不需要数据标签,仅靠特征值就可以将数据分为指定的几类.k-means算法的核心就是通过计算每个数据点与k个质心(或重心)之间的 ...