本文将演示怎么通过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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
dy("nrwz");
查看更多关于C#基于Windows服务的聊天程序(1)的详细内容...