惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
V
V2EX
博客园 - 【当耐特】
Vercel News
Vercel News
雷峰网
雷峰网
爱范儿
爱范儿
WordPress大学
WordPress大学
云风的 BLOG
云风的 BLOG
S
Securelist
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Microsoft Azure Blog
Microsoft Azure Blog
F
Full Disclosure
有赞技术团队
有赞技术团队
Hugging Face - Blog
Hugging Face - Blog
NISL@THU
NISL@THU
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Attack and Defense Labs
Attack and Defense Labs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
Microsoft Security Blog
Microsoft Security Blog
腾讯CDC
P
Proofpoint News Feed
B
Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
K
Kaspersky official blog
I
InfoQ
Google Online Security Blog
Google Online Security Blog
L
LINUX DO - 最新话题
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
V
Visual Studio Blog
AI
AI
Schneier on Security
Schneier on Security
B
Blog RSS Feed
T
Tor Project blog
H
Help Net Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LINUX DO - 热门话题
阮一峰的网络日志
阮一峰的网络日志
S
Security @ Cisco Blogs
T
Threat Research - Cisco Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
C
Cyber Attacks, Cyber Crime and Cyber Security
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
V2EX - 技术
V2EX - 技术
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
A
Arctic Wolf
Webroot Blog
Webroot Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main

博客园 - dataxiu.com

[原创.数据可视化系列之十三]idw反距离权重插值算法的javascript代码实现 [原创.数据可视化系列之十二]使用 nodejs通过async await建立同步数据抓取 [原创.数据可视化系列之八]使用等d3进行灰度图转伪彩色 [原创.数据可视化系列之七]阿里竞赛作品技术展示 [原创.数据可视化系列之六]使用openlyaers进行公网地图剪切 [原创.数据可视化系列之五]韩国"萨德"系统防御图 [原创.数据可视化系列之四]跨平台,多格式的等值线和等值面的生成 [原创.数据可视化系列之三]使用Ol3加载大量点数据 [原创.数据可视化系列之二]使用cesium三维地图展示美国全球军事基地分布 [原创.数据可视化系列之一]使用openlayers 3 显示聚合数据 管理类软件的界面模板。 使用SilverLight开发ARPG游戏(一) 买联想学生机器的遭遇:问联想1(连载) 联想09年春节学生机开卖了。 谈UrlRewriter在XP和2003上IIS设置的差异 使用SilverLight构建插件式应用程序(九) —聊天插件客户端的实现 .NET 访问JAVA的WebService使用SOAP头 使用SilverLight构建插件式应用程序(七) 使用SilverLight构建插件式应用程序(六)
使用SilverLight构建插件式应用程序(八) —聊天插件Duplex WCF的实现
dataxiu.com · 2008-10-22 · via 博客园 - dataxiu.com

这次是一个在线聊天的插件,插件参考了MSDN中Duplex WCF服务的实现和网上一些聊天程序,基本可以实现用户登录和聊天,如果用户不存在就保存聊天数据到数据库,等用户下次登陆的时候读入。

这个是聊天时候的图例:


1: 定义聊天时候传输聊天的数据实体,这个所有WCF都是一样的,没什么特殊的,代码如下:

[DataContract]
public class ChatMessage
{
    [DataMember]
    public string From;
    [DataMember]
    public string To;
    [DataMember]
    public int Type;//1:Text;2:Url;3:File;4:Image;5:Audio;6:Video;0:None;-1:System;-2:Heart;
    [DataMember]
    public string Data;
    [DataMember]
    public byte[] Content;

}

这样用户每次聊天就发送的使这个数据了。这是服务器端的定义。

2:定义回调时候的接口:

[ServiceContract]

public interface IWSChatClient
{
    [OperationContract(IsOneWay = true)]
    void Receive(Message returnMessage);

}

 3:定义Duplex WCF服务的实现,

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceContract(Namespace = "WindCloud", SessionMode = SessionMode.Required, CallbackContract = typeof(IWSChatClient))]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class WSChatService
{
    static Dictionary<string, IWSChatClient> chatClients = new Dictionary<string, IWSChatClient>();
    static List<string> m_NameList = new List<string>();
    private static Object syncObj = new Object();
    static int guestIndex = 1;

    string name = "";

}

这个WCF服务并没有从接口继承过来,而是直接地实现,这个和MSDN上边稍微有点差异,之所以这样做,因为将来可以使用Forms验证模式来验证用户,这是一个主要的原因,将来说到权限的时候,我会具体的说到这一点。

4: 接受心跳信息:

    /// <summary>
    /// 获取心跳信息更新连接
    /// </summary>
    /// <param name="receivedMessage"></param>
    [OperationContract(IsOneWay = true)]
    public void Heart(Message receivedMessage)
    {
        ChatMessage chatMessage = receivedMessage.GetBody<ChatMessage>();
        IWSChatClient chatclient = OperationContext.Current.GetCallbackChannel<IWSChatClient>();
        if (chatClients.ContainsKey(chatMessage.From))
        {
            chatClients[chatMessage.From] = chatclient;
            //
            chatMessage.From = "SystemServer";
            chatMessage.To = chatMessage.From;
            chatMessage.Type = -2;
            Message returnMessage = Message.CreateMessage(MessageVersion.Soap11, "WindCloud/WSChatService/Receive", chatMessage);
            chatclient.Receive(returnMessage);
        }
    }

客户端会定时发送心跳信息到服务器端,如果客户端有段时间没有发送心跳信息,这就是说客户端掉线或者其他原因了。

5:用户进入聊天室:

//进入聊天室
    [OperationContract(IsOneWay = true)]
    public void Join(Message receivedMessage)
    {
        bool userAdded = false;
        Message returnMessage;

        ChatMessage chatMessage = receivedMessage.GetBody<ChatMessage>();

        IWSChatClient chatclient = OperationContext.Current.GetCallbackChannel<IWSChatClient>();

        ChatMessage sendChatMessage = new ChatMessage();
        sendChatMessage.From = "Server:";
        sendChatMessage.Data = "Service:请等待...";
        sendChatMessage.Type = -1;

        //
        lock (syncObj)
        {          
            if (!chatClients.ContainsKey(chatMessage.From) && chatMessage.From != "" && chatMessage.From != null)
            {
                chatMessage.Data = "Service:开始添加用户...";
                returnMessage = Message.CreateMessage(MessageVersion.Soap11, "WindCloud/WSChatService/Receive", chatMessage);

                chatclient.Receive(returnMessage);

                this.name = chatMessage.From;
                m_NameList.Add(chatMessage.From);
                chatClients.Add(chatMessage.From, chatclient);
                userAdded = true;
            }
            else
            {
                ChatMessage cm = new ChatMessage();
                cm.From = "Server:";
                cm.Data = "Service:User:" + chatMessage + "加入失败,用户已存在。";
                returnMessage = Message.CreateMessage(MessageVersion.Soap11, "WindCloud/WSChatService/Receive", cm);
                //
                chatclient.Receive(returnMessage);

                return;
            }
            //发送留言
            ChatBLL bll = new ChatBLL();
            List<ChatMessage> cms = bll.GetChats(chatMessage.From);
            foreach (ChatMessage cm in cms)
            {
                SendMessageToClient(cm);
            }
            //删除留言
            bll.Delete(chatMessage.From);
        }

        if (userAdded)
        {
            string text2 = "Service:User:" + chatMessage + " is already Joined";
            ChatMessage cm = new ChatMessage();
            cm.From = "Server:";
            cm.Data = text2;
            SendMessageToClient(cm);
        }
    }

 用户加入聊天室之后,加入到用户队列中,同时从用户聊天的数据库读入其他用户给当前用户的留言并删除留言。

6:用户说话,这是平常一直要使用的方法,接受用户的聊天信息:

  /// <summary>
    /// 聊天
    /// </summary>
    /// <param name="receivedMessage"></param>
    [OperationContract(IsOneWay = true)]
    public void Say(Message receivedMessage)
    {
        IWSChatClient chatclient = OperationContext.Current.GetCallbackChannel<IWSChatClient>();
        ChatMessage text = receivedMessage.GetBody<ChatMessage>();

        if (!m_NameList.Contains(text.To))
        {
            //保存数据到数据库
            ChatBLL bll = new ChatBLL();
            bll.Add(text);
            return;
        }       
        if (!chatClients.ContainsValue(chatclient))
        {
            return;
        }
        SendMessageToClient(text);
    }

代码很少 ,如果用户没有登陆,就把聊天信息保存到数据库,否则,直接发送到用户的客户端去。

 7:用户退出聊天室:

   }
    /// <summary>
    /// 退出聊天室
    /// </summary>
    /// <param name="receivedMessage"></param>
    [OperationContract(IsOneWay = true)]
    public void Exit(Message receivedMessage)
    {
        IWSChatClient client = OperationContext.Current.GetCallbackChannel<IWSChatClient>();
        ChatMessage cm = receivedMessage.GetBody<ChatMessage>();

        if (chatUsers.Contains(cm.From))
        {
            chatUsers.Remove(cm.From);
        }
        if (chatClients.ContainsKey(cm.From))
        {
            chatClients.Remove(cm.From);
        }

    }

代码就是直接删除了队列中的用户。

公共函数:发送数据到客户端:

  private void SendMessageToClient(ChatMessage chatMessage)
    {
        if (chatMessage.To != null && chatClients.ContainsKey(chatMessage.To))
        {
            IWSChatClient chatclient = chatClients[chatMessage.To];
            Message returnMessage = Message.CreateMessage(MessageVersion.Soap11, "WindCloud/WSChatService/Receive", chatMessage);
            try
            {
                chatclient.Receive(returnMessage);
            }
            catch
            {
                if (chatUsers.Contains(chatMessage.To))
                {
                    chatUsers.Remove(chatMessage.To);
                }
                if (chatClients.ContainsKey(chatMessage.To))
                {
                    chatClients.Remove(chatMessage.To);
                }
            }
        }

    }

如果发送的时候发生异常,就从队列删除。

8:ServerHost的代码,这个从MSDN上抄过来的:

public class ChatHostFactory : ServiceHostFactoryBase
{
    public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
    {
        return new ChatServiceHost(baseAddresses);
    }
}

class ChatServiceHost : ServiceHost
{
    public ChatServiceHost(params System.Uri[] addresses)
    {
        base.InitializeDescription(typeof(WSChatService), new UriSchemeKeyedCollection(addresses));
    }

    protected override void InitializeRuntime()
    {
        // Define the binding and set time-outs.
        PollingDuplexBindingElement pdbe = new PollingDuplexBindingElement()
        {
            ServerPollTimeout = TimeSpan.FromSeconds(10),
            InactivityTimeout = TimeSpan.FromMinutes(1)
        };

        // Add an endpoint for the given service contract.
        this.AddServiceEndpoint(
            typeof(WSChatService),
            new CustomBinding(
                pdbe,
                new TextMessageEncodingBindingElement(
                    MessageVersion.Soap11,
                    System.Text.Encoding.UTF8),
                new HttpTransportBindingElement()),
                "");

        base.InitializeRuntime();
    }
}

9:svc文件的代码:

<%@ServiceHost language=c# Debug="true" Factory="ChatHostFactory" %>

这个也是从MSDN上抄写过来的。

读写数据库的代码其实可以完全注释掉,这个只是为了有个保存功能。整个代码还有不完善的地方,但是基本可以实现Duplex WCF的从服务器向客户端推数据的功能,保证数据实时,这也是长久也来我想了解的服务器和客户端实时通信的问题的一个解决办法。