东儿童
爱拼才会赢

利用了WCF的Socket实现在线聊天室的服务器端

 
文/monkeyboyabc  出处/博客园

1.建立WCF Service,命名为ChatService
添加引用,

 
CODE:
System.ServiceModel.dll
System.ServiceModel.PollingDuplex.dll
System.Runtime.Serialization.dll


2.定义接口IChatService.cs
CODE:
    [ServiceContract(Namespace = "Silverlight",CallbackContract = typeof(IChatCallback),SessionMode = SessionMode.Required)]
    public interface IChat
    {

        [OperationContract(IsOneWay = true)]
        void Order(Message receivedMessage);

        [OperationContract(IsOneWay = true)]
        void JoinChat(Message receivedMessage);

        [OperationContract(IsOneWay = true)]
        void SayChat(Message receivedMessage);
    }


相对应的callback接口
 
CODE:
    [ServiceContract]
    public interface IChatCallback
    {
        [OperationContract(IsOneWay = true)]
        void Receive(Message returnMessage);
    }


注意:在IChat中的属性 NameSpace一定要是SiliverLight,且在SessionMode中 要标明 SessionMode.Required 否则会出错

3.在Service文件中 定义服务
在文件ChatService.cs中添加以下代码:
 
CODE:
static Dictionary<string, IChatCallback> chatClients = new Dictionary<string, IChatCallback>();
        static List<string> m_NameList = new List<string>();
        static int guestIndex = 1;

        public void JoinChat(Message receivedMessage)
        {
            IChatCallback chatclient;
            bool userAdded = false;
            string test;
            Message returnMessage;

            string name = receivedMessage.GetBody<string>();

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

            test = "Service:JoiningPlease Wait";
            returnMessage = Message.CreateMessage(MessageVersion.Soap11,
                "Silverlight/IChat/Receive", test);

            chatclient.Receive(returnMessage);

            lock (syncObj)
            {
                if (name == "Guest")
                {
                    name = "Guest" + guestIndex.ToString();
                    guestIndex = guestIndex + 1;
                }
                if (!chatClients.ContainsKey(name) && name != "" && name != null)
                {
                    test = "Service:Begin Add User To ChattersList";
                    returnMessage = Message.CreateMessage(MessageVersion.Soap11,
                        "Silverlight/IChat/Receive", test);

                    chatclient.Receive(returnMessage);

                    this.name = name;
                    m_NameList.Add(name);
                    chatClients.Add(name, chatclient);
                    userAdded = true;
                                     
                }
                else
                {
                    test = "Service:User:" + name + "Join fault.Already has this user or User name is NULL";

                    returnMessage = Message.CreateMessage(MessageVersion.Soap11,
                        "Silverlight/IChat/Receive",test );

                    chatclient.Receive(returnMessage);

                    return;
                }
            }

            if (userAdded)
            {
                string text2 = "Service:User:" + name + " is already Joined";

                SendMessageToClient(text2);
            }
        }


这个方法是对应接口中的JoinChat,将用户添加到会话列表中。
其中,调用Receive接口将Message返回
然后,遍利用户字典,将信息发送给所有用户
 
CODE:
        public void SayChat(Message receivedMessage)
        {
            IChatCallback chatclient = OperationContext.Current.GetCallbackChannel<IChatCallback>();
            string text = receivedMessage.GetBody<string>();

            if(!chatClients.ContainsValue(chatclient))
            {
                return;
            }

            //TODO: 用更好的方法实现
            for (int i = 0; i < m_NameList.Count; i++)
            {
                IChatCallback clientInList = chatClients[m_NameList];

                if (clientInList.Equals(chatclient))
                {
                    text = m_NameList + " Says: " + text;
                    break;
                }
            }

            SendMessageToClient(text);
        }

        private void SendMessageToClient(string test)
        {
            //TODO: 用更好的方法实现
            for (int i = 0; i < m_NameList.Count; i++)
            {
                IChatCallback chatclient = chatClients[m_NameList];

                Message returnMessage = Message.CreateMessage(MessageVersion.Soap11,
                    "Silverlight/IChat/Receive", test);

                chatclient.Receive(returnMessage);
            }
        }


4.添加宿主文件PollingDuplexServiceHostFactory.cs
 
CODE:
using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Channels;

namespace SLChat
{
    public class PollingDuplexServiceHostFactory : ServiceHostFactoryBase
    {
        public override ServiceHostBase CreateServiceHost(string constructorString,
            Uri[] baseAddresses)
        {
            //return new PollingDuplexSimplexServiceHost(baseAddresses);

            PollingDuplexSimplexServiceHost serviceHost = new PollingDuplexSimplexServiceHost(baseAddresses);

            System.ServiceModel.Description.ServiceMetadataBehavior smb = serviceHost.Description.Behaviors.Find<System.ServiceModel.Description.ServiceMetadataBehavior>();
            if (smb != null)
            {
                smb.HttpGetEnabled = true;
               
            }
            else
            {
                smb = new System.ServiceModel.Description.ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                serviceHost.Description.Behaviors.Add(smb);
            }

            return serviceHost;
        }
    }

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

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

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

            base.InitializeRuntime();
        }
    }
}


5.一定不要忘记了 添加一个跨域访问的文件**(折磨我尽一天的问题。。。)
clientaccesspolicy.xml
 
CODE:
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="*">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>


没有这个文件,在Windows自带的IIS中将发布不了,但是在VS2008的IIS是没有问题的

6.在VS的资源管理器(Solution Explore)右键点击ChatService.svc,点击View MarkUp
将原来的代码修改为:
 
CODE:
<%@ServiceHost language=c# Debug="true" Service="SLChat.ChatService" Factory="SLChat.PollingDuplexServiceHostFactory"%>


使Service用我自己写的Host发布 而不用配置文件
posted on 2008-09-15 14:37  哎!无悔  阅读(810)  评论(0编辑  收藏  举报