(一)SuperSocket(windows服务+webform)


(一)服务安装过程

1.点击ProjectInstaller.cs,在点击serviceProcessInstaller1修改Account值为LocalSystem;

在点击serviceInstaller1,修改StartType为Automatic(手动启动服务),修改ServiceName设置成你想要的服务名称,比如:testwebsocket

2.安装服务

 cmd输入:cd C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319

然后安装服务:InstallUtil ""E:\网站项目ssssssssss\学习资料(白天)\ef\codefirst,sqlsugar,dapper,websocket混合\websocket\windowsService(windows服务得websocket)\WebsocketService\WebsocketService\bin\Debug\WebsocketService.exe"

3:删除服务

sc delete testwebsocket

(二)前端逻辑

前端页面逻辑



提示:

提示:

提示:
















    前端js逻辑

    (function (w) {

    //(一)心跳检测,检测后端是否还可以链接

    var heartCheck = {//心跳检测
    timeout: 10000,
    timeoutObj: null,
    servetTimeoutObj: null,
    start: function () {
    var self = this;
    this.timeoutObj && clearTimeout(this.timeoutObj);//如果不加this.timeoutObj &&,clearTimeout以后timeoutObj对象就不存在了,再次setTimeout时就会因找不到对象报错
    this.servetTimeoutObj && clearTimeout(this.servetTimeoutObj);//同上
    this.timeoutObj = setTimeout(function () {

    //10秒后执行,执行成功后,在ws.onmessage里面调用heartCheck.start();继续心跳检测,此时clearTimeout(this.servetTimeoutObj)下面的servetTimeoutObj就被清掉了,不会执行了

    //如果执行失败,就不会调用heartCheck.start();不会执行clearTimeout(this.servetTimeoutObj),就会打印 您暂时无法获取服务端推送的最新消息,请检查网络是否异常! 这句话
    ws.send("heartcheck");

    self.servetTimeoutObj = setTimeout(function () {
    $("#j_heart").append("您暂时无法获取服务端推送的最新消息,请检查网络是否异常!")
    ws.close();
    //google(会进onclose函数),在onclose里面调用重连方法mainUitls.reconnect();,重连失败进入onerror函数,在onerror里面反复重连操作如果没连上
    //ie
    //直接进入onerror函数,在reconnect函数李设定的时间后再次从连
    }, self.timeout)
    }, this.timeout)

    }
    }

    //声明全局对象
    var ws;
    var lockReconnect = false;
    var tt = null;
    var mainUitls = {

    //(二)初始话基本事件

    init: function (dom) {
    this.createWebsocket(dom);
    this.initClick();

    },

    //(三)重连操作

    reconnect: function () {
    if (lockReconnect) {//防止多次创建链接,只有当一个链接创建有了结果才允许下次创建,防止多次执行创建链接的操作(被多个事件执行调用)
    return;
    };
    lockReconnect = true;
    tt && clearTimeout(tt);//将tt设置为underfind,如果直接clearTimeout(tt) ,tt就被清掉不存在了,下次给tt赋值就会报错
    tt = setTimeout(function () {//进入该方法20秒后进行重连,链接失败后在进入ws.onerror继续重连
    mainUitls.createWebsocket($("#j_userKey").val());
    lockReconnect = false;
    }, 20000)
    },

    //(四)创建长链接,以及跟链接相关的事件

    createWebsocket: function (dom) { 

    //1:建立长链接:

    ws = new WebSocket('ws://192.168.2.117:8200');//就是后端的ip,8200就是后端的端口

    //2:链接成功后要做心跳检测

    ws.onopen = function () {
    $('#j_notice').html('已经连接');

    //链接成功后把当前用户id推上去(windows服务没法像ashx那样传参数所以只能推上去,而且userid每次发送都要重新推上去,第二个socket的userid会覆盖第一个的socket)

     ws.send(dom + "--userid");
    heartCheck.start();//链接成功以后开始心跳检测
    }

    //3:接收服务端或者其他客户端推送过来的消息

    ws.onmessage = function (evt) {
    if (evt.data == "heartchecksuccess") {
    $("#j_heart").append("

  • 链接正常
  • ");
    heartCheck.start();//接受到服务器信息表示链接是通的,过一会儿再次进行心跳检测
    } else {
    $("#j_Msg").append("
  • " + evt.data + "
  • ");
    }
    }

    //4:网络异常后过一会需要重连操作,就是突然网络就好了

    ws.onerror = function (evt) {
    $('#j_wlyc').append("链接出现异常,请检查网络!");
    mainUitls.reconnect();
    }

    //5:链接关闭后要执行的函数,比如很久没得到服务器的响应,可以暂时断开,过一会在进行链接reconnect();

    ws.onclose = function () {
    $('#j_notice').html("连接断开");
    mainUitls.reconnect();
    }

    },

    //(五)各种点击事件

    initClick: function () {

    //1:关闭链接

    $("#j_close").on("click", function () {
    ws.close();
    });

    //2:群发消息

    $("#j_send").on("click", function () {
    if ($("#j_userKey").val() == "") {
    $('#j_notice').html("请输入用户标识");
    return;
    }
    //表示与服务器已经建立好连接
    if (ws.readyState == WebSocket.OPEN) {
    var content = $("#j_userKey").val() + "--alluser" + $('#j_content').val();
    ws.send(content);
    }
    //表示与服务器连接已经断开
    else if (ws.readyState == WebSocket.CLOSED) {
    $('#j_notice').html('与服务器连接已经断开');
    }
    //表示正在尝试与服务建立连接
    else if (ws.readyState == WebSocket.CONNECTING) {
    $('#j_notice').html('正在尝试与服务建立连接');
    }
    //正在关闭与服务器连接
    else if (ws.readyState == WebSocket.CLOSING) {
    $('#j_notice').html('正在关闭与服务器连接');
    }

    });

    //3:单发消息

    $("#j_send2").on("click", function () {
    if ($("#j_userKey").val() == "") {
    $('#j_notice').html("请输入登陆标识");
    return;
    }
    var reciveiduser = $('#j_receiveUserKey').val();
    if (reciveiduser == "") {
    $('#j_notice').html('请输入接收人的标识');
    return;
    }
    //下面对内容进行拼接
    var loginuser = $("#j_userKey").val();
    var finalMsg = loginuser + "--touserid" + reciveiduser + "--touserid" + $('#j_content').val();
    //表示与服务器已经建立好连接
    if (ws.readyState == WebSocket.OPEN) {
    ws.send(finalMsg);

    }
    //表示与服务器连接已经断开
    else if (ws.readyState == WebSocket.CLOSED) {
    $('#j_notice').html('与服务器连接已经断开');
    }
    //表示正在尝试与服务建立连接
    else if (ws.readyState == WebSocket.CONNECTING) {
    $('#j_notice').html('正在尝试与服务建立连接');
    }
    //正在关闭与服务器连接
    else if (ws.readyState == WebSocket.CLOSING) {
    $('#j_notice').html('正在关闭与服务器连接');
    }

    });

    };

    w.mainUitls = mainUitls;
    })(window)

    //(六)初始化方法,并生成随机数据的用户标识

    $(function () {
    var dom1 = Math.ceil(Math.random() * 10);//6;
    $("#j_userKey").val(dom1)
    mainUitls.init(dom1);

    });

    (三)后端逻辑

    //首先在sindows福利的App.config里面添加




    //在windows服务开启的事件里面写(相当于业务层)

    protected override void OnStart(string[] args)
    {

    var ip = ConfigurationManager.AppSettings["APWebSocketIP"];
    var port = ConfigurationManager.AppSettings["APWebSocketPort"];
    SuperWebSocket.WebSocketServer server = new SuperWebSocket.WebSocketServer();
    if (!server.Setup(ip, int.Parse(port)))
    {
    //处理启动失败消息,可以写log错误日志
    return;
    }

    server.NewSessionConnected += WebSocketServer_NewSessionConnected;//用户上线
    server.SessionClosed += WebSocketServer_SessionClosed;//用户下线
    server.NewMessageReceived += WebSocketServer_NewMessageReceived;//接收消息


    if (!server.Start())
    {
    //处理监听失败消息,可以写log错误日志
    return;
    }

    //接收消息

    private void WebSocketServer_NewMessageReceived(WebSocketSession session, string value)
    {

    if (value.Contains("--userid"))//第一次,把userid作为key,socket作为值存起来
    {
    #region
    WebsocketObj websocketObj = new WebsocketObj();
    websocketObj.userid = value.Replace("--userid", "");
    websocketObj.webSocket = session;
    WebManager.AddWebsocket(websocketObj);//添加连接池
    #region 用户链接成功,表示用户已经上线
    string remote_ip = ((System.Net.IPEndPoint)session.RemoteEndPoint).Address.MapToIPv4().ToString();//获取远程连接IP
    //string msg=string.Format("WebSocketManager:用户{0} {1}上线", value.Replace("--userid", ""), remote_ip);//可以写到log日志
    #endregion
    #endregion

    }
    else if (value.Contains("--touserid"))//给单个用户发送信息
    {

    #region
    var array = value.Split(new string[] { "--touserid" }, StringSplitOptions.None);
    string currentuserid = array[0];//当前得用户id
    string recieveuserid = array[1];//接收人得用户id
    string msg = array[2];//接收得内容
    string errpr;
    msg = currentuserid + "发来消息:" + msg;
    WebManager.SendToSimple(recieveuserid, msg, out errpr);
    if (!string.IsNullOrWhiteSpace(errpr))
    {
    //string msg=errpr;//发送异常信息可以写入log
    }
    #endregion
    }
    else if (value.Contains("heartcheck"))//心跳检测
    {

    session.Send("heartchecksuccess");//给客户端回复信息,表示客户端和服务端还能相互通讯
    }
    else//群发消息
    {
    #region 
    var array = value.Split(new string[] { "--alluser" }, StringSplitOptions.None);
    string currentuserid = array[0];//当前得用户id
    string msg = array[1];//接收得内容
    string errpr;
    msg = currentuserid + "发来消息:" + msg;
    WebManager.SendToAll(currentuserid, msg, out errpr);
    if (!string.IsNullOrWhiteSpace(errpr))
    {
    //WriteLog(errpr);//发送异常打印异常信息
    }
    #endregion
    }

    //用户下线,可以找到关闭的原因和远程链接的ip

    private void WebSocketServer_SessionClosed(WebSocketSession session, SuperSocket.SocketBase.CloseReason value)
    {

    string remote_ip = ((System.Net.IPEndPoint)session.RemoteEndPoint).Address.MapToIPv4().ToString();//获取远程连接IP
    string msg=string.Format("WebSocketManager:{0} {1}下线", value, remote_ip);//可以写入log日志

    //用户上线,可以获取到是哪个远程ip上线了

    private void WebSocketServer_NewSessionConnected(WebSocketSession session)
    {
    string remote_ip = ((System.Net.IPEndPoint)session.RemoteEndPoint).Address.MapToIPv4().ToString();//可以写入log日志
    }

    (四)后端底层实现代码

    public static class WebManager
    {

    public static List websocketObjs = new List();//如果是分布式需要存到redis里面

     //用户第一次和后端握手,创建链接

    public static void AddWebsocket(WebsocketObj websocketObj)
    {
    websocketObjs.Add(websocketObj);
    }

    //给指定的单个用户发送信息

    public static void SendToSimple(string userid, string msg, out string error)
    {
    error = "";
    List websocketObjs1 = websocketObjs.Where(m => m.userid == userid).ToList();
    if (websocketObjs1.Count > 0)
    {
    for (int i = 0; i < websocketObjs1.Count; i++)
    {
    try
    {
    websocketObjs1[i].webSocket.Send(msg);
    }
    catch (Exception e)
    {
    error = websocketObjs1[i].userid+":"+e.Message;//如果出现异常
    }

    }
    }

    //给除了自己以外的所有人发送信息

    public static void SendToAll(string userid, string msg, out string error)
    {
    error = "";
    List websocketObjs1 = websocketObjs.Where(m => m.userid != userid).ToList();
    if (websocketObjs1.Count > 0)
    {
    for (int i = 0; i < websocketObjs1.Count; i++)
    {
    try
    {
    websocketObjs1[i].webSocket.Send(msg);
    }
    catch (Exception e)
    {
    error = websocketObjs1[i].userid + ":" + e.Message;//如果出现异常
    }

    }
    }

    //存socket的类

    public class WebsocketObj
    {
    public string userid { get; set; }
    public WebSocketSession webSocket { get; set; }
    }