InChatter系统之服务器开发(二)
现在我们继续进行InChatter系统的服务器端的开发,今天我们将实现服务契约同时完成宿主程序的开发,今天结束之后服务器端将可以正常运行起来。
系统的开发是随着博客一起的,颇有点现场直播的感觉,所有在写博的过程中,可能会回头重新讲解和修复以前的设计不合理的地方,同时也可能会融合新的想法以及功能模块,提前跟各位看客交代下,请大家见谅。不过我想这个过程对大家也是有利的,在这个过程中,一是带大家重新回顾一下以前的设计想法并与现在进行比较,二是可以增长大家的项目设计的感觉,增长经验,这也是项目开发中不可避免的。所以,这也是我坚持直播的原因,如果文章中有什么不对的地方或者修改意见,欢迎大家指正,好的修改建议我会在开发过程中融入进来。
本人文笔较差,表达可能不够详尽,如果什么不节的地方,欢迎大家指正。
一、服务器端的开发
我们修改了服务契约,增加了一个GetOnlineClient的方法
[OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false)]
string GetOnlineClient();
通过这个方法,我们可以获取除了自己以外其他的所有在线客户端,同时修改了Login方法的返回值,从而我们可以得到登录的反馈状态
[OperationContract(IsOneWay=false,IsInitiating=true,IsTerminating=false)]
bool Login(string clientId);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using InChatter.Service.Data; namespace InChatter.Service
{
public class Chat : IChat
{
public static Dictionary<string, IChatCallback> Callbacks = new Dictionary<string, IChatCallback>();
public bool Login(string clientId)
{
if (Callbacks.Keys.Contains(clientId))
{
return false;
}
IChatCallback callback;
//告知其他用户,新用户上线
BroadcastUserState(clientId, true);
callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
Callbacks.Add(clientId, callback);
//返回在线客户端
return true;
} public string GetOnlineClient()
{
IChatCallback callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
//获取除本客户端以外的其他在线客户端
var result = Callbacks.Where(p => p.Value != callback).Select(p => p.Key).ToList();
return string.Join(",", result);
} public void SendMsg(Data.InChatterMessage message)
{
if (message == null)
{
return;
}
if (message.Type == "notice")
{
SendNotice(message);
}
else if (message.Type == "msg")
{
try
{
Callbacks[message.ReceiverID].ReceiveMsg(message);
}
catch
{
Logout(message.ReceiverID);
}
}
} public void Logout(string clientId)
{
try
{
lock (Callbacks)
{
//移除退出用户的callBacks
Callbacks.Remove(clientId);
}
//广播下线消息
BroadcastUserState(clientId, false);
}
catch
{ }
} /// <summary>
/// 通知其他用户上线或者下线消息
/// </summary>
/// <param name="employeeId"></param>
/// <param name="isLogin"></param>
private void BroadcastUserState(string clientId, bool isLogin)
{
List<string> list = new List<string>();
foreach (var item in Callbacks)
{
InChatterMessage txt = new InChatterMessage();
txt.Content = "";
txt.SenderID = clientId;
txt.ReceiverID = item.Key;
if (isLogin)
{
txt.Type = "logon";
}
else
{
txt.Type = "logoff";
}
txt.SendTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
try
{
item.Value.ReceiveMsg(txt);
}
catch
{
list.Add(item.Key);
}
}
RemoveDisconnectCallBacks(list);
} /// <summary>
/// 发送通知消息,接受者为所有在线客户
/// </summary>
/// <param name="msg"></param>
private void SendNotice(InChatterMessage msg)
{
List<string> list = new List<string>();
foreach (var item in Callbacks)
{
try
{
item.Value.ReceiveMsg(msg);
}
catch
{
list.Add(item.Key);
}
}
RemoveDisconnectCallBacks(list);
} /// <summary>
/// 移除断开连接客户端
/// </summary>
/// <param name="list"></param>
private void RemoveDisconnectCallBacks(List<string> list)
{
if (list.Count > )
{
//移除出错的callBack
foreach (var item in list)
{
Callbacks.Remove(item);
}
//将连接通道出错的项认定为下线,发送下线消息给在线客户端
foreach (var item in list)
{
BroadcastUserState(item, false);
}
}
}
}
}
如果我们定义一个关于Chat类的一个构造函数,那么我们可以看到客户端与服务器建立回话时,都会调用构造函数初始化一个Chat的实例,也就是说每一个客户端与服务器端对应的一个Chat类交互。
在Chat类中我们定义一个静态的全局变量Callbacks,我们在该变量中存储客户端标识Id和对应的回调实体,这样便可以在所有的Chat类中操作我们需要的回调,来完成对指定客户端的操作。
二、宿主程序的实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks; namespace InChatter.Service.Host
{
class Program
{
static void Main(string[] args)
{
Uri baseUri = new Uri("http://localhost:1378/InChatter");
using (ServiceHost host = new ServiceHost(typeof(Chat), baseUri))
{
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.None;
//会话保持时间
binding.ReceiveTimeout = TimeSpan.FromHours();
host.AddServiceEndpoint(typeof(IChat), binding, "net.tcp://localhost:1121/InChatter");
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
host.Opened += host_Opened;
try
{
host.Open(); }
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Press 'exit' to exit!");
string enterStr = Console.ReadLine();
while (enterStr.ToLower() != "exit")
{
enterStr = Console.ReadLine();
}
}
} public static void host_Opened(object sender, EventArgs e)
{
Console.WriteLine("Service Opened!");
}
}
}
启动运行,在程序没有错误的情况下,如果防火墙开启,则会有如下提示:
这个代表我们的程序没有错误,但是运行以后呢?
系统出错了,提示不具有命名空间访问权限,其实就是我们的程序需要以管理员身份运行。
OK,成功了!
这里我们通过代码实现了整个服务的寄宿,同时我们也可以使用配置文件来实现:
下面我们是WCF配置工具来实现客户端的配置:
1.打开WCF服务配置编辑器
2.找到服务契约生成的dll,点击打开
3.选中我们的服务实现类型
4.点击下一步,在类型中选择TCP
5.进入下一步,输入终结点地址,如下所示
5.点击下一步,并完成,保存配置后,将配置文件复制到宿主程序的目录下,覆盖默认的App.config
生成的配置文件如下:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<services>
<service name="InChatter.Service.Chat">
<endpoint address="net.tcp://localhost:1121/InChatter"
binding="netTcpBinding"
bindingConfiguration="" contract="InChatter.Service.IChat" />
</service>
</services>
</system.serviceModel>
</configuration>
代码调用也比较简单:
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading.Tasks; namespace InChatter.Service.Host
{
class Program
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(Chat)))
{
host.Opened += host_Opened;
try
{
host.Open();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
} public static void host_Opened(object sender, EventArgs e)
{
Console.WriteLine("Service Opened!");
}
}
}
同样需要以管理员方式启动程序!
但是目前的配置文件存在一定的问题,稍后在客户端处理的时候,我们再详细讲解~
服务端已经可以正常运行了,源码提供给大家:下载源码(到CodePlex下载最新版本)
InChatter系统之服务器开发(二)的更多相关文章
- InChatter系统之服务器开发(一)
服务器端是整个消息系统的中枢,类似与人类的大脑.没有他,根本无法实现客户端之间的交流,为什么呢?这也涉及到我们的系统涉及,在服务器端,每个客户端的标识数据都会在服务器端进行保存,在这种情况下,当某一个 ...
- Python服务器开发二:Python网络基础
Python服务器开发二:Python网络基础 网络由下往上分为物理层.数据链路层.网络层.传输层.会话层.表示层和应用层. HTTP是高层协议,而TCP/IP是个协议集,包过许多的子协议.包括: ...
- linux服务器开发二(系统编程)--线程相关
线程概念 什么是线程 LWP:Light Weight Process,轻量级的进程,本质仍是进程(在Linux环境下). 进程:独立地址空间,拥有PCB. 线程:也有PCB,但没有独立的地址空间(共 ...
- linux服务器开发二(系统编程)--进程相关
进程相关的概念 程序与进程 程序,是指编译好的二进制文件,在磁盘上,不占用系统资源(CPU.内存.打开的文件.设备.锁等等). 进程,是一个抽象的概念,与操作系统原理联系紧密.进程是活跃的程序,占用系 ...
- EasyDSS点播与直播服务器软件-二次开发接口对接说明示列
EasyDSS流媒体服务器软件,提供一站式的转码.点播.直播.时移回放服务,极大地简化了开发和集成的工作.其中,点播版本主要包含:上传.转码.分发.直播版本,主要包含:直播.录像, 直播支持RTMP输 ...
- EasyDSS高性能RTMP、HLS(m3u8)、HTTP-FLV、RTSP流媒体服务器软件二次开发接口对接说明示列
EasyDSS相关功能 EasyDSS流媒体服务器软件,提供一站式的转码.点播.直播.时移回放服务,极大地简化了开发和集成的工作.其中,点播版本主要包含:上传.转码.分发.直播版本主要包含:直播.录像 ...
- JAVA+PHP+阿里云组件纯手工实现POP、SMTP、IMAP开发邮件服务器(二)
java开发邮件服务器的接收模块 用java建立socket服务端,监听端口25,实现SMTP协议.即可完成邮件服务器的接收模块. 这里要注意的是,SMTP协议其实可以分为两种.一种是你用手机.PC等 ...
- FastAPI(七十二)实战开发《在线课程学习系统》接口开发-- 留言列表开发
之前我们分享了FastAPI(七十一)实战开发<在线课程学习系统>接口开发-- 查看留言,这次我们分享留言列表开发. 列表获取,也需要登录,根据登录用户来获取对应的留言.逻辑梳理如下. 1 ...
- C++服务器开发之笔记三
为什么需要原子性操作? 我们考虑一个例子:(1)x++这个常见的运算符在内存中是怎样操作的?从内存中读x的值到寄存器中,对寄存器加1,再把新值写回x所处的内存地址 若是有两个线程同时对同一个变量++, ...
随机推荐
- jupyter环境的安装
1,什么是jupyter notebook? 简介:jupyter notebook是基于网页的用户交互计算机的应用程序,其可被用于全过程计算:开发,文档编写,运行代码,和展示结果 简而言之,Jupy ...
- make eval builtin function
1 eval的返回值是空字符串,因此它可以用于Makefile的任何位置而不引起错误 2 eval函数的作用效果 生成Makefile的动态部分,即eval用于增加Makefile的构成部分. 也就是 ...
- Class.forName() 详解
主要功能 Class.forName(xxx.xx.xx)返回的是一个类 Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类, 也就是说JVM会执行该类的静态代码段 ...
- beyond compare 比较文本 standard alignment VS unaligned
在Rules里面 Standard Alignment 这种方式会比较找出相同的部分,可能会跨行找相同的 Unaligned 这种比较直接每一行之间相互比较,不跨行找相同的
- YTU 1007: Redraiment猜想
1007: Redraiment猜想 时间限制: 1000 Sec 内存限制: 10 MB 提交: 83 解决: 23 题目描述 redraiment在家极度无聊,于是找了张纸开始统计素数的个数. ...
- ACTION 关联表之间查询语句 SQL语句写法
/** EquUseRecord * @author cll * @return * @右边菜单中的使用记录操作 */ public String QueryAllEquUserecordAllInf ...
- js页面报错javax.servlet.jsp.PageContext cannot be resolved to a type解决
构建了一个maven项目但是项目创建好的jsp总会报错javax.servlet.jsp.PageContext cannot be resolved to a type,但是不影响项目运行.但总归难 ...
- 微信公众平台——基础配置——服务器配置:PHP版
在自己的服务器上新建一个空白php文件,输入以下任一版本的代码,如下: 版本一: <?php $token = "dige1994"; $signature = $_GET[ ...
- Centos6可以ping通但浏览器不能上网(解决)
遇到这种情况,只需要修改一下DNS Server即可,如下图. 这样再试试,应该可以了吧-
- python 模块 module 规范
# /usr/bin/python # -*- coding=utf-8 -*- """This is a standard module""&quo ...