利用了WCF的Socket实现在线聊天室的服务器端
文/monkeyboyabc 出处/博客园
1.建立WCF Service,命名为ChatService
添加引用,
CODE:
2.定义接口IChatService.cs
相对应的callback接口
CODE:
注意:在IChat中的属性 NameSpace一定要是SiliverLight,且在SessionMode中 要标明 SessionMode.Required 否则会出错
3.在Service文件中 定义服务
在文件ChatService.cs中添加以下代码:
CODE:
这个方法是对应接口中的JoinChat,将用户添加到会话列表中。
其中,调用Receive接口将Message返回
然后,遍利用户字典,将信息发送给所有用户
CODE:
4.添加宿主文件PollingDuplexServiceHostFactory.cs
CODE:
5.一定不要忘记了 添加一个跨域访问的文件**(折磨我尽一天的问题。。。)
clientaccesspolicy.xml
CODE:
没有这个文件,在Windows自带的IIS中将发布不了,但是在VS2008的IIS是没有问题的
6.在VS的资源管理器(Solution Explore)右键点击ChatService.svc,点击View MarkUp
将原来的代码修改为:
CODE:
使Service用我自己写的Host发布 而不用配置文件
1.建立WCF Service,命名为ChatService
添加引用,
System.ServiceModel.dll
System.ServiceModel.PollingDuplex.dll
System.Runtime.Serialization.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);
}
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接口
[ServiceContract]
public interface IChatCallback
{
[OperationContract(IsOneWay = true)]
void Receive(Message returnMessage);
}
public interface IChatCallback
{
[OperationContract(IsOneWay = true)]
void Receive(Message returnMessage);
}
注意:在IChat中的属性 NameSpace一定要是SiliverLight,且在SessionMode中 要标明 SessionMode.Required 否则会出错
3.在Service文件中 定义服务
在文件ChatService.cs中添加以下代码:
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);
}
}
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返回
然后,遍利用户字典,将信息发送给所有用户
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);
}
}
{
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
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();
}
}
}
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
<?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>
<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
将原来的代码修改为:
<%@ServiceHost language=c# Debug="true" Service="SLChat.ChatService" Factory="SLChat.PollingDuplexServiceHostFactory"%>
使Service用我自己写的Host发布 而不用配置文件