|
内容摘要
1.程序架构
2.通信协议
3.服务器源代码
4.客户端源代码
5.运行效果
一、程序架构
在开发一个聊天室程序时,我们可以使用Socket、Remoting、WCF这些具有双向通信的协议或框架。而现在,我正要实现一个C#语言作为服务器端、Android作为客户端的聊天室。由于服务器端和客户端不是同一语言(C#和java),所有我选择了Socket作为通信协议。
图1.1所示,我们可以看出:android手机客户端A向服务器端发送消息,服务器端收到消息后,又把消息推送到android手机客户端B。
我们知道,在C#语言中使用Socket技术需要“四部曲”,即“Bind”,“Listen”,“Accept”,“Receive”。然而Socket编程不像WCF那样面向对象。而且对应每个请求都用同一种方式处理。作为习惯面向对象编程的我来说,编写一个传统的Socket程序很不爽。绞尽脑汁,我们将数据传输的格式改为json(JavaScript Object Notation 是一种轻量级的数据交换格式),面对对象的问题就解决了。
假设程序的服务契约有两个方法:“登陆”和“发送消息”。调用登陆的方法,就传送方法名(Method Name)为“Logon”的json数据;调用发送消息的方法,就传送方法名为“Send”的json数据。返回的数据中也使用json格式,这样在android客户端中也能知道是哪个方法的返回值了。
三、服务器源代码
首先需要编写一个处理客户端消息的接口:IResponseManager。
public interface IResponseManager
{
void Write(Socket sender, IList<Socket> cliens, IDictionary<string, object> param);
}
复制代码其次,我们知道,换了是WCF编程的话,就需要在服务契约中写两个方法:“登陆”和“发送消息”。由于这里是Socket编程,我们实现之前写的IResponseManager接口,一个实现作为“登陆”的方法,另一个实现作为“发送消息”的方法。
public class LogonResponseManager : IResponseManager
{
public void Write(System.Net.Sockets.Socket sender, IList<System.Net.Sockets.Socket> cliens, IDictionary<string, object> param)
{
Console.WriteLine("客户端({0})登陆", sender.Handle);
var response = new SocketResponse
{
Method = "Logon",
DateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
Result = new { UserName = param["UserName"].ToString() }
};
JavaScriptSerializer jss = new JavaScriptSerializer();
string context = jss.Serialize(response);
Console.WriteLine("登陆发送的数据为:{0}", context);
sender.Send(Encoding.UTF8.GetBytes(context + "\n"));
}
}
public class SendResponseManager : IResponseManager
{
public void Write(System.Net.Sockets.Socket sender, IList<System.Net.Sockets.Socket> cliens, IDictionary<string, object> param)
{
Console.WriteLine("客户端({0})发送消息", sender.Handle);
var msgList = param["Message"] as IEnumerable<object>;
if (msgList == null)
{
return;
}
var response = new SocketResponse
{
Method = "Send",
DateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
Result = new
{
UserName = param["UserName"].ToString(),
Message = msgList.Select(s => s.ToString()).ToArray()
}
};
JavaScriptSerializer jss = new JavaScriptSerializer();
string context = jss.Serialize(response);
Console.WriteLine("消息发送的数据为:{0}", context);
Parallel.ForEach(cliens, (item) =>
{
try
{
item.Send(Encoding.UTF8.GetBytes(context + "\n"));
}
catch { };
});
}
}
复制代码最后在Socket程序中使用反射加“策略模式”调用这两个接口实现类。
var typeName = "SocketServer." + request.Method + "ResponseManager, SocketServer";
Console.WriteLine("反射类名为:" + typeName);
Type type = Type.GetType(typeName);
if (type == null)
{
return;
}
var manager = Activator.CreateInstance(type) as IResponseManager;
manager.Write(sender, this.socketClientSesson.Select(s => s.Key).ToList(),
request.Param as IDictionary<string, object>);
复制代码完整的Socket服务器代码如下: public class SocketHost
{
private IDictionary<Socket, byte[]> socketClientSesson = new Dictionary<Socket, byte[]>();
public int Port { get; set; }
public void Start()
{
var socketThread = new Thread(() =>
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, this.Port);
//绑定到通道上
socket.Bind(iep);
//侦听
socket.Listen(6);
//通过异步来处理
socket.BeginAccept(new AsyncCallback(Accept), socket);
});
socketThread.Start();
Console.WriteLine("服务器已启动");
}
private void Accept(IAsyncResult ia)
{
Socket socket = ia.AsyncState as Socket;
var client = socket.EndAccept(ia);
socket.BeginAccept(new AsyncCallback(Accept), socket);
byte[] buf = new byte[1024];
this.socketClientSesson.Add(client, buf);
try
{
client.BeginReceive(buf, 0, buf.Length, SocketFlags.None, new AsyncCallback(Receive), client);
string sessionId = client.Handle.ToString();
Console.WriteLine("客户端({0})已连接", sessionId);
}
catch (Exception ex)
{
Console.WriteLine("监听请求时出错:\r\n" + ex.ToString());
}
}
private void Receive(IAsyncResult ia)
{
var client = ia.AsyncState as Socket;
if (client == null || !this.socketClientSesson.ContainsKey(client))
{
return;
}
int count = client.EndReceive(ia);
byte[] buf = this.socketClientSesson[client];
if (count > 0)
{
try
{
client.BeginReceive(buf, 0, buf.Length, SocketFlags.None, new AsyncCallback(Receive), client);
string context = Encoding.UTF8.GetString(buf, 0, count);
Console.WriteLine("接收的数据为:", context);
this.Response(client, context);
}
catch (Exception ex)
{
Console.WriteLine("接收的数据出错:\r\n{0}", ex.ToString());
}
}
else
{
try
{
string sessionId = client.Handle.ToString();
client.Disconnect(true);
this.socketClientSesson.Remove(client);
Console.WriteLine("客户端({0})已断开", sessionId);
}
catch (Exception ex)
{
Console.WriteLine("客户端已断开出错" + ex.ToString());
}
}
}
private void Response(Socket sender, string context)
{
SocketRequest request = null;
JavaScriptSerializer jss = new JavaScriptSerializer();
request = jss.Deserialize(context, typeof(SocketRequest)) as SocketRequest;
if (request == null)
{
return;
}
var typeName = "SocketServer." + request.Method + "ResponseManager, SocketServer";
Console.WriteLine("反射类名为:" + typeName);
Type type = Type.GetType(typeName);
if (type == null)
{
return;
}
var manager = Activator.CreateInstance(type) as IResponseManager;
manager.Write(sender, this.socketClientSesson.Select(s => s.Key).ToList(),
request.Param as IDictionary<string, object>);
}
}
复制代码最后,json数据传输的实体对象为: [Serializable]
public class SocketRequest
{
public string Method { get; set; }
public string DateTime { get; set; }
public object Param { get; set; }
}
[Serializable]
public class SocketResponse
{
public string Method { get; set; }
public string DateTime { get; set; }
public object Result { get; set; }
}
复制代码 |
|