本章主要实现TCP服务端功能的实现,客户端功能实现参见https://blog.csdn.net/liwan09/article/details/106320716
一、项目创建
1、VS2017创建winform项目
2、Nuget搜索安装SuperSocket.Engine
二、服务端功能实现
1、由于supersockect依赖log4net,需要对log4net进行设置,参考https://blog.csdn.net/liwan09/article/details/106266346
2、界面设计
3、 后端代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Forms;
using System.Configuration;
using log4net;
using SuperSocket.SocketBase;
using SuperSocket.SocketBase.Protocol;
using SuperSocket.SocketBase.Config;
using Bosch.Rtns.Sockect.Model;
using Bosch.Rtns.Sockect.CommonUtils;
namespace Bosch.Rtns.Sockect
{
public partial class frmMain : Form
{
AppServer appServer;
ILog loggerManager;
Dictionary<AppSession, string> socketSessionList = new Dictionary<AppSession, string>();//记录链接的客户端数据
List<FaultSend> faultSends = new List<FaultSend>();//记录接收的故障消息
private System.Threading.Timer sendMsgTimer;//消息定时发送
string webapiUrl = ConfigurationManager.AppSettings["WebApiUrl"].ToString();//webapi接口地址
int timerInterval = Convert.ToInt32(ConfigurationManager.AppSettings["TimeInterval"].ToString());//定时发送消息间隔 单位秒
int sendMsgTimes = Convert.ToInt32(ConfigurationManager.AppSettings["SendTimes"].ToString());//消息发送的次数
public frmMain()
{
InitializeComponent();
}
/// <summary>
/// load
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void frmMain_Load(object sender, EventArgs e)
{
loggerManager =LogManager.GetLogger("Logger");
txtIPAddress.Text = "127.0.0.1";
txtPort.Text = "4141";
btnEnd.Enabled = false;
}
/// <summary>
///开启服务
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
string ipAddress = txtIPAddress.Text.Trim();
string port = txtPort.Text.Trim();
if (string.IsNullOrEmpty(ipAddress))
{
txtIPAddress.Focus();
MessageBox.Show("请输入IP地址!");
return;
}
if (string.IsNullOrEmpty(port))
{
txtPort.Focus();
MessageBox.Show("请输入端口号!");
return;
}
ServerConfig serverConfig = new ServerConfig();
serverConfig.Ip = ipAddress;
serverConfig.Port = Convert.ToInt32(port);
serverConfig.TextEncoding = "gbk";
appServer = new AppServer();
appServer.NewSessionConnected += Sockect_NewSessionConnected;
appServer.NewRequestReceived += Sockect_NewMessageReceived;
appServer.SessionClosed += Sockect_SessionClosed;
if (!appServer.Setup(serverConfig))
{
txtLogInfo.Text += "设置Socket服务侦听地址失败!\r\n";
loggerManager.Error("设置Socket服务侦听地址失败!\r\n");
return;
}
if (!appServer.Start())
{
txtLogInfo.Text += "启动Socket服务侦听失败!\r\n";
loggerManager.Error("启动Socket服务侦听失败!\r\n");
return;
}
txtLogInfo.Text += "Socket启动服务成功!\r\n";
loggerManager.Info("Socket启动服务成功!\r\n");
txtIPAddress.Enabled = false;
txtPort.Enabled = false;
btnStart.Enabled = false;
btnEnd.Enabled = true;
//开启定时消息推送
sendMsgTimer = new System.Threading.Timer(obj=>SendMsgTimer(),null, 200,timerInterval*1000);
}
/// <summary>
/// 关闭服务
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnEnd_Click(object sender, EventArgs e)
{
if (appServer != null)
{
appServer.Stop();
appServer = null;
socketSessionList.Clear();
}
faultSends.Clear();
if (sendMsgTimer != null)
{
sendMsgTimer.Dispose();
sendMsgTimer = null;
}
txtIPAddress.Enabled = true;
txtPort.Enabled = true;
btnStart.Enabled = true;
btnEnd.Enabled = false;
}
/// <summary>
/// 新的Socket连接事件
/// </summary>
/// <param name="session"></param>
private void Sockect_NewSessionConnected(AppSession session)
{
string logMsg = string.Format("{0} 与客户端{1}创建新会话", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), GetwebSocketSessionName(session))+"\r\n";
this.Invoke(new Action(() =>
{
txtLogInfo.Text += logMsg;
}));
loggerManager.Info(logMsg);
socketSessionList.Add(session, "");
}
/// <summary>
/// 消息接收事件
/// </summary>
/// <param name="session"></param>
/// <param name="value">接收的消息</param>
private void Sockect_NewMessageReceived(AppSession session,StringRequestInfo stringRequestInfo )
{
string receiveMsg = stringRequestInfo.Key;
string logMsg = string.Format("{0} 从客户端{1}接收新的消息:\r\n{2}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), GetwebSocketSessionName(session), receiveMsg) + "\r\n";
this.Invoke(new Action(() =>
{
txtLogInfo.Text += logMsg;
}));
loggerManager.Info(logMsg);
#region 接收数据处理
if (!string.IsNullOrEmpty(receiveMsg))
{
try
{
ReceiveData receiveData = JsonHelper.ConvertToEntity<ReceiveData>(receiveMsg);
string msgContent = receiveData.MessageContent;
if (receiveData.MessageType == "2")//设备故障推送
{
EquipmentFault equipmentFault = JsonHelper.ConvertToEntity<EquipmentFault>(msgContent);
if (equipmentFault.IsRead == 0)
{
FaultSend faultSend = new FaultSend();
faultSend.EquipmentFault = equipmentFault;
faultSend.FaultString = msgContent;
faultSends.Add(faultSend);
var postResult = CommonUtils.HttpRequest.HttpPost(webapiUrl + "EquipmentFault/Save", msgContent);//调用webapi,数据库保存数据
}
else
{
//已读的数据
var data = faultSends.Where(x=>x.EquipmentFault.Equals(equipmentFault)).FirstOrDefault();
if (data != null)
{
faultSends.Remove(data);
}
}
}
else
{
AppDeviceData appDeviceData = JsonHelper.ConvertToEntity<AppDeviceData>(msgContent);
socketSessionList[session] = appDeviceData.DeviceID;
}
}
catch(Exception ex)
{
string logErrorMsg = string.Format("Error:{0}",ex.Message) + "\r\n";
this.Invoke(new Action(() =>
{
txtLogInfo.Text += logErrorMsg;
}));
loggerManager.Info(logErrorMsg);
}
}
#endregion
}
/// <summary>
/// sockect客户端断开连接事件
/// </summary>
/// <param name="session"></param>
/// <param name="value"></param>
private void Sockect_SessionClosed(AppSession session, SuperSocket.SocketBase.CloseReason value)
{
string logMsg = string.Format("{0} 与客户端{1}的会话被关闭 原因:{2}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), GetwebSocketSessionName(session), value) + "\r\n";
this.Invoke(new Action(() =>
{
txtLogInfo.Text += logMsg;
}));
loggerManager.Info(logMsg);
//判断socketSessionList中的deviceid是否为空,不为空的话,考虑是否有正在推送的消息,如果有,那么停止消息推送
string deviceId = "";
if(socketSessionList.TryGetValue(session, out deviceId))
{
socketSessionList.Remove(session);
}
//消息推送中,移除当前设备的推送消息
faultSends = faultSends.Where(x=>!x.EquipmentFault.TargetPhoneID.Equals(deviceId)).ToList();
}
/// <summary>
/// 获取连接的客户端的ip地址
/// </summary>
/// <param name="webSocketSession"></param>
private static string GetwebSocketSessionName(AppSession appSession)
{
var remoteClient = appSession.RemoteEndPoint;
return remoteClient.Address + ":" + remoteClient.Port.ToString();
//return HttpUtility.UrlDecode(appSession.SessionID);
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="session"></param>
/// <param name="msg"></param>
private void SendMessage(AppSession session, string msg)
{
session.Send(msg);
//向所有服务端发送消息
//foreach (var sendSession in session.AppServer.GetAllSessions())
//{
// sendSession.Send(msg);
//}
}
/// <summary>
/// 定时发送消息
/// </summary>
private void SendMsgTimer()
{
this.Invoke(new Action(()=> {
for(int i = 0; i < faultSends.Count(); i++)
{
var sendTime = faultSends[i].SendTime;
if (sendTime<sendMsgTimes)
{
string targetPhoneID = faultSends[i].EquipmentFault.TargetPhoneID;
AppSession targetSession = socketSessionList.Where(x => x.Value.Equals(targetPhoneID) && x.Value != "").Select(x => x.Key).FirstOrDefault();
if (targetSession != null)
{
loggerManager.Info("向设备【" + targetPhoneID + "】推送消息\r\n");
SendMessage(targetSession, faultSends[i].FaultString);
faultSends[i].SendTime++;
}
}
}
faultSends = faultSends.Where(x => x.SendTime < sendMsgTimes).ToList();//更新消息推送列表
}));
}
}
}
4、其他相关代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bosch.Rtns.Sockect.Model
{
/// <summary>
/// App端传输的数据
/// </summary>
public class AppDeviceData
{
/// <summary>
/// 登录设备的ID
/// </summary>
public string DeviceID { get; set; }
/// <summary>
/// 登录的账户名称
/// </summary>
public string LoginName { get; set; }
/// <summary>
/// 剩余电量
/// </summary>
public string BatteryLeft { get; set; }
/// <summary>
/// 在线状态 0:不在线 1在线 默认在线
/// </summary>
public int IsOnline { get; set; } = 1;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bosch.Rtns.Sockect.Model
{
/// <summary>
/// 设备故障信息
/// </summary>
public class EquipmentFault
{
/// <summary>
/// 目标手机设备ID
/// </summary>
public string TargetPhoneID { get; set; }
/// <summary>
/// 故障发生时间
/// </summary>
public DateTime FaultTime { get; set; }
/// <summary>
/// 消息id
/// </summary>
public string MessageUID { get; set; }
/// <summary>
/// 消息标题
/// </summary>
public string MessageTitle { get; set; }
/// <summary>
/// 发送给App(消息类型)
/// </summary>
public string SubAPPName { get; set; }
/// <summary>
/// 故障等级 Low,Default,High,Emergent
/// </summary>
public string EmergenceLevel { get; set; }
/// <summary>
/// 是否已读 0:否 1:是 默认否
/// </summary>
public int IsRead { get; set; } = 0;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bosch.Rtns.Sockect.Model
{
/// <summary>
/// 故障消息推送
/// </summary>
public class FaultSend
{
/// <summary>
/// 故障信息
/// </summary>
public EquipmentFault EquipmentFault { get; set; }
/// <summary>
/// 故障发送次数
/// </summary>
public int SendTime { get; set; }
/// <summary>
/// 设备故障信息Json字符串
/// </summary>
public string FaultString { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Bosch.Rtns.Sockect.Model
{
/// <summary>
/// 接收的消息
/// </summary>
public class ReceiveData
{
/// <summary>
/// 接收的消息类型 1:App端登录 2:设备故障信息推送
/// </summary>
public string MessageType { get; set; }
/// <summary>
/// 消息内容
/// </summary>
public string MessageContent { get; set; }
}
}