馬上加入Android 台灣中文網,立即免費下載應用遊戲。
您需要 登錄 才可以下載或查看,沒有帳號?註冊
x
內容摘要
1.程序架構
2.通信協議
3.服務器源代碼
4.客戶端源代碼
5.運行效果
一、程序架構
在開發一個聊天室程序時,我們可以使用Socket、Remoting、WCF這些具有雙向通信的協議或框架。而現在,我正要實現一個C#語言作為服務器端、Android作為客戶端的聊天室。由於服務器端和客戶端不是同一語言(C#和java),所有我選擇了Socket作為通信協議。 圖1.1所示,我們可以看出:android手機客戶端A向服務器端發送消息,服務器端收到消息後,又把消息推送到android手機客戶端B。
圖1.1
二、通信協議
我們知道,在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; }
- }
複製代碼 運行效果
|
評分
-
查看全部評分

|