当前位置: 移动技术网 > IT编程>开发语言>c# > C#基于Windows服务的聊天程序(1)

C#基于Windows服务的聊天程序(1)

2019年07月18日  | 移动技术网IT编程  | 我要评论

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

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

自定义协议:

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.net\framework\v4.0.30319;%path%
installutil %~dp0\windowschat.exe
%systemroot%\system32\sc failure "chatservice" reset= 30 actions= restart/1000
pause
@echo on

uninstall.bat
@echo off
path %systemroot%\microsoft.net\framework\v4.0.30319;%path%
installutil -u %~dp0\windowschat.exe
pause
@echo on

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

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

6.回到上面的重写onstart和onstop方法

创建一个sockethelper类

namespace windowschat
{
  public delegate void writeinfo(string info);

  public class sockethelper
  {
    #region 构造函数
    public sockethelper()
    {
    }
    public sockethelper(writeinfo method)
    {
      this.method = method;
    }
    #endregion

    public static socket localsocket = null;
    private object lockobj = new object();
    public static list<socket> clients = new list<socket>();
    private writeinfo method = null;

    /// <summary>
    /// 创建socket
    /// </summary>
    /// <param name="port">端口默认 11011</param>
    /// <param name="backlog">the maximum length of the pending connections queue.</param>
    /// <returns></returns>
    public socket create(int port = 11011, int backlog = 100)
    {
      if (localsocket == null)
      {
        ipendpoint ipendpoint = new ipendpoint(ipaddress.any, port);//本机预使用的ip和端口
        localsocket = new socket(addressfamily.internetwork, sockettype.stream, protocoltype.tcp);
        localsocket.bind(ipendpoint);
        localsocket.listen(backlog);
      }
      return localsocket;
    }

    /// <summary>
    /// 查找客户端连接
    /// </summary>
    /// <param name="id">标识</param>
    /// <returns></returns>
    private socket findlinked(string id)
    {
      foreach (var item in clients)
      {
        if (item.remoteendpoint.tostring() == id)
          return item;
      }
      return null;
    }

    /// <summary>
    /// 接受远程连接
    /// </summary>
    public void accept()
    {
      if (localsocket != null)
      {
        while (true)
        {
          socket client = localsocket.accept();
          thread thread = new thread(new parameterizedthreadstart(revice));
          thread.start(client);
          writelog("客户端:" + client.remoteendpoint.tostring() + " 接入");
          lock (lockobj)
          {
            clients.add(client);
          }
          broadcast("add|" + client.remoteendpoint.tostring());
        }
      }
    }

    /// <summary>
    /// 日志
    /// </summary>
    /// <param name="info">信息</param>
    private void writelog(string info)
    {
      using (filestream fs = new filestream("c:\\chatservice.txt", filemode.append, fileaccess.write, fileshare.readwrite))
      {
        using (streamwriter sw = new streamwriter(fs, encoding.utf8))
        {
          sw.writeline(info);
        }
      }
      if (method != null)
      {
        method(info);
      }
    }

    /// <summary>
    /// 广播
    /// </summary>
    /// <param name="info">信息</param>
    public void broadcast(string info)
    {
      foreach (var item in clients)
      {
        try
        {
          item.send(encoding.utf8.getbytes(info));
        }
        catch (exception ex)
        {
          writelog(item.remoteendpoint.tostring() + ex.message);
          continue;
        }
      }
    }

    /// <summary>
    /// 介绍信息
    /// </summary>
    /// <param name="client"></param>
    public void revice(object client)
    {
      socket param = client as socket;
      var remotename = param.remoteendpoint.tostring();
      if (param != null)
      {
        int res = 0;
        while (true)
        {
          byte[] buffer = new byte[10240];
          int size = param.receivebuffersize;
          try
          {
            res = param.receive(buffer);
          }
          catch (socketexception ex)
          {
            if (ex.socketerrorcode == socketerror.connectionreset)
            {
              clients.remove(param);
              writelog("客户端:" + remotename + "断开连接1");
              broadcast("remove|" + remotename);
              param.close();
              return;
            }
          }

          if (res == 0)
          {
            clients.remove(param);
            writelog("客户端:" + remotename + "断开连接2");
            broadcast("remove|" + remotename);
            param.close();
            return;
          }
          var clientmsg = encoding.utf8.getstring(buffer, 0, res);
          writelog(string.format("收到客户端{0}命令:{1}", remotename, clientmsg));
          if (clientmsg == "getall")
          {
            stringbuilder sb = new stringbuilder();
            foreach (var item in clients)
            {
              sb.appendformat("{0}|", item.remoteendpoint.tostring());
            }
            param.send(encoding.utf8.getbytes("all|" + sb.tostring()));
          }
          else if (clientmsg == "offline")
          {
            if (clients.contains(param))
            {
              clients.remove(param);
              writelog("客户端:" + remotename + "断开连接2");
              broadcast("remove|" + remotename);
              param.close();
              return;
            }
          }
          else if (clientmsg.startswith("transt|"))
          {
            var msgs = clientmsg.split('|');
            var tosocket = findlinked(msgs[1]);
            if (tosocket != null)
            {
              writelog(remotename + "发给" + msgs[1] + "的消息" + msgs[2]);
              tosocket.send(encoding.utf8.getbytes("transf|" + remotename + "|" + msgs[2]));
            }
          }
        }
      }
    }
  }
}

重写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:\bin\

则部署脚本为

  cd c:\bin\

  installutil windowschat.exe

  卸载脚本

  installutil -u windowschat.exe

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网