zoukankan      html  css  js  c++  java
  • 基于Windows服务的聊天程序

      本文将演示怎么通过C#开发部署一个Windows服务,该服务提供各客户端的信息通讯,适用于局域网。采用TCP协议,单一服务器连接模式为一对多;多台服务器的情况下,当客户端连接数超过预设值时可自动进行负载转移,当然也可手动切换服务器,这种场景在实际项目中应用广泛。

      简单的消息则通过服务器转发,文件类的消息则让客户端自己建立连接进行传输。后续功能将慢慢完善。

      自定义协议:

    标识

    含义

    参数

    From

    GETALL

    获取所有在线终端

     

    Client

    OFFLINE

    客户端下线

     

    Client

    SHUTDOWN

    服务器下线

     

    Server

    ALL|{0}

    在线终端

    {0}所有在线客户端标识|分隔

    Server

    REMOVE|{0}

    通知下线

    {0}下线客户端的标识

    Server

    TRANST|{0}|{1}

    发送消息

    {0}接受消息客户端的标识{1}消息

    Client

    TRANSF|{0}|{1}

    发送消息

    {0}发送消息客户端的标识{1}消息

    Server

    BROADCAST

    广播

     

    Server

    FILE

    文件

     

    Client

    LINKTO|{0}

    连接

     

    Server

     

     

     

      1.新建Windows服务项目

      

      2.修改配置文件添加

    <appSettings>
        <add key="maxQueueCount" value="10"/>
        <add key="failoverServer" value="192.168.250.113,192.168.250.141"/>
    </appSettings>

           说明:maxQueueCount为最大连接数,failoverServer故障转移备用服务器(多个服务器,隔开)

      3.打开ChatService右键添加安装程序,此时会自动添加ProjectInstaller.cs文件,文件中会默认添加serviceProcessInstaller1和serviceInstaller1两个组件

      

      修改serviceInstaller1和serviceProcessInstaller1的属性信息如图

      

      

      StartType属性说明:

      Automatic 指示服务在系统启动时将由(或已由)操作系统启动。如果某个自动启动的服务依赖于某个手动启动的服务,则手动启动的服务也会在系统启动时自动启动。

      Disabled 指示禁用该服务,以便它无法由用户或应用程序启动。

      Manual 指示服务只由用户(使用“服务控制管理器”)或应用程序手动启动。

      Account属性说明:

      LocalService    充当本地计算机上非特权用户的帐户,该帐户将匿名凭据提供给所有远程服务器。

      LocalSystem    服务控制管理员使用的帐户,它具有本地计算机上的许多权限并作为网络上的计算机。

      NetworkService    提供广泛的本地特权的帐户,该帐户将计算机的凭据提供给所有远程服务器。

      User    由网络上特定的用户定义的帐户。如果为 ServiceProcessInstaller.Account 成员指定 User,则会使系统在安装服务时提示输入有效的用户名和密码,除非您为 ServiceProcessInstaller 实例的 Username 和 Password 这两个属性设置值。

      4.完成以后打开ChatService代码,重写OnStart和OnStop方法(即服务的启动和停止方法)。若要重写其它方法请在ServiceBase中查看。

      5.在项目中添加服务注册和卸载脚本文件

    Install.bat
    @echo off
    path %SystemRoot%Microsoft.NETFrameworkv4.0.30319;%path%
    installutil %~dp0WindowsChat.exe
    %SystemRoot%system32sc failure "ChatService" reset= 30 actions= restart/1000
    pause
    @echo on
    
    Uninstall.bat
    @echo off
    path %SystemRoot%Microsoft.NETFrameworkv4.0.30319;%path%
    installutil -u %~dp0WindowsChat.exe
    pause
    @echo on

      说明:%~dp0 表示bat文件所在的目录

      文件属性选择 始终复制-内容,这样才能生成到输出文件夹中

       

      6.回到上面的重写OnStart和OnStop方法

      创建一个SocketHelper类

      1 namespace WindowsChat
      2 {
      3     public delegate void WriteInfo(string info);
      4 
      5     public class SocketHelper
      6     {
      7         #region 构造函数
      8         public SocketHelper()
      9         {
     10         }
     11         public SocketHelper(WriteInfo method)
     12         {
     13             this.method = method;
     14         }
     15         #endregion
     16 
     17         public static Socket LocalSocket = null;
     18         private object lockObj = new object();
     19         public static List<Socket> Clients = new List<Socket>();
     20         private WriteInfo method = null;
     21 
     22         /// <summary>
     23         /// 创建Socket
     24         /// </summary>
     25         /// <param name="port">端口默认 11011</param>
     26         /// <param name="backlog">The maximum length of the pending connections queue.</param>
     27         /// <returns></returns>
     28         public Socket Create(int port = 11011, int backlog = 100)
     29         {
     30             if (LocalSocket == null)
     31             {
     32                 IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, port);//本机预使用的IP和端口
     33                 LocalSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
     34                 LocalSocket.Bind(ipEndPoint);
     35                 LocalSocket.Listen(backlog);
     36             }
     37             return LocalSocket;
     38         }
     39 
     40         /// <summary>
     41         /// 查找客户端连接
     42         /// </summary>
     43         /// <param name="id">标识</param>
     44         /// <returns></returns>
     45         private Socket FindLinked(string id)
     46         {
     47             foreach (var item in Clients)
     48             {
     49                 if (item.RemoteEndPoint.ToString() == id)
     50                     return item;
     51             }
     52             return null;
     53         }
     54 
     55         /// <summary>
     56         /// 接受远程连接
     57         /// </summary>
     58         public void Accept()
     59         {
     60             if (LocalSocket != null)
     61             {
     62                 while (true)
     63                 {
     64                     Socket client = LocalSocket.Accept();
     65                     Thread thread = new Thread(new ParameterizedThreadStart(Revice));
     66                     thread.Start(client);
     67                     WriteLog("客户端:" + client.RemoteEndPoint.ToString() + " 接入");
     68                     lock (lockObj)
     69                     {
     70                         Clients.Add(client);
     71                     }
     72                     BroadCast("ADD|" + client.RemoteEndPoint.ToString());
     73                 }
     74             }
     75         }
     76 
     77         /// <summary>
     78         /// 日志
     79         /// </summary>
     80         /// <param name="info">信息</param>
     81         private void WriteLog(string info)
     82         {
     83             using (FileStream fs = new FileStream("C:\chatservice.txt", FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
     84             {
     85                 using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8))
     86                 {
     87                     sw.WriteLine(info);
     88                 }
     89             }
     90             if (method != null)
     91             {
     92                 method(info);
     93             }
     94         }
     95 
     96         /// <summary>
     97         /// 广播
     98         /// </summary>
     99         /// <param name="info">信息</param>
    100         public void BroadCast(string info)
    101         {
    102             foreach (var item in Clients)
    103             {
    104                 try
    105                 {
    106                     item.Send(Encoding.UTF8.GetBytes(info));
    107                 }
    108                 catch (Exception ex)
    109                 {
    110                     WriteLog(item.RemoteEndPoint.ToString() + ex.Message);
    111                     continue;
    112                 }
    113             }
    114         }
    115 
    116         /// <summary>
    117         /// 介绍信息
    118         /// </summary>
    119         /// <param name="client"></param>
    120         public void Revice(object client)
    121         {
    122             Socket param = client as Socket;
    123             var remoteName = param.RemoteEndPoint.ToString();
    124             if (param != null)
    125             {
    126                 int res = 0;
    127                 while (true)
    128                 {
    129                     byte[] buffer = new byte[10240];
    130                     int size = param.ReceiveBufferSize;
    131                     try
    132                     {
    133                         res = param.Receive(buffer);
    134                     }
    135                     catch (SocketException ex)
    136                     {
    137                         if (ex.SocketErrorCode == SocketError.ConnectionReset)
    138                         {
    139                             Clients.Remove(param);
    140                             WriteLog("客户端:" + remoteName + "断开连接1");
    141                             BroadCast("REMOVE|" + remoteName);
    142                             param.Close();
    143                             return;
    144                         }
    145                     }
    146 
    147                     if (res == 0)
    148                     {
    149                         Clients.Remove(param);
    150                         WriteLog("客户端:" + remoteName + "断开连接2");
    151                         BroadCast("REMOVE|" + remoteName);
    152                         param.Close();
    153                         return;
    154                     }
    155                     var clientMsg = Encoding.UTF8.GetString(buffer, 0, res);
    156                     WriteLog(string.Format("收到客户端{0}命令:{1}", remoteName, clientMsg));
    157                     if (clientMsg == "GETALL")
    158                     {
    159                         StringBuilder sb = new StringBuilder();
    160                         foreach (var item in Clients)
    161                         {
    162                             sb.AppendFormat("{0}|", item.RemoteEndPoint.ToString());
    163                         }
    164                         param.Send(Encoding.UTF8.GetBytes("ALL|" + sb.ToString()));
    165                     }
    166                     else if (clientMsg == "OFFLINE")
    167                     {
    168                         if (Clients.Contains(param))
    169                         {
    170                             Clients.Remove(param);
    171                             WriteLog("客户端:" + remoteName + "断开连接2");
    172                             BroadCast("REMOVE|" + remoteName);
    173                             param.Close();
    174                             return;
    175                         }
    176                     }
    177                     else if (clientMsg.StartsWith("TRANST|"))
    178                     {
    179                         var msgs = clientMsg.Split('|');
    180                         var toSocket = FindLinked(msgs[1]);
    181                         if (toSocket != null)
    182                         {
    183                             WriteLog(remoteName + "发给" + msgs[1] + "的消息" + msgs[2]);
    184                             toSocket.Send(Encoding.UTF8.GetBytes("TRANSF|" + remoteName + "|" + msgs[2]));
    185                         }
    186                     }
    187                 }
    188             }
    189         }
    190     }
    191 }
    SocketHelper

      重写OnStart和OnStop方法

    public partial class ChatService : ServiceBase
    {
            SocketHelper helper;
            Thread mainThread;
    
            public ChatService()
            {
                InitializeComponent();
            }
    
            protected override void OnStart(string[] args)
            {
                if (helper == null)
                {
                    helper = new SocketHelper();
                }
                helper.Create();
                mainThread = new Thread(new ThreadStart(helper.Accept));
                mainThread.IsBackground = true;
                mainThread.Start();
            }
    
            protected override void OnStop()
            {
                helper.BroadCast("SHUTDOWN");
            }
    }

      至此一个简易的Windows服务的聊天服务端开发完成,后续会在这基础上进行扩展。

      运行install.bat(以管理员身份运行)如图

      

      7.运行 services.msc查找到ChatService服务,能正常启动停止说明部署成功!

      

      当然你也可以将InstallUtil.exe拷贝到执行文件所在目录,比如c:in

      则部署脚本为

      cd c:in

      InstallUtil WindowsChat.exe

      卸载脚本

      InstallUtil -u WindowsChat.exe

    本文地址:http://www.cnblogs.com/liuxiaobo93/p/7205904.html   未经允许不得转载!

  • 相关阅读:
    IDEA快捷键
    关于redis key命名规范的设计 【转载】
    Mybatis数据源与连接池 【转载】
    通过缓冲区读文件和非缓冲区读文件有什么区别
    BCB6 使用正则表达式的例子
    QT在子窗口外单击关闭子窗口
    QT5 中文乱码
    QT 鼠标右键菜单
    关于线程池的几个问题
    关于ConcurentHashMap的几个问题
  • 原文地址:https://www.cnblogs.com/liuxiaobo93/p/7205904.html
Copyright © 2011-2022 走看看