標籤:最簡 連接埠 nbsp keyword basic ref href ack port
websocket是HTML5中的比較有特色一塊,它使得以往在用戶端軟體中常用的socket在web程式中也能輕鬆的使用,較大的提高了效率。廢話不多說,直接進入題。
網頁聊天室包括2個部分,後端伺服器+前端頁面。
1、後端服務部分:.net4.0 + windows服務。相比寄宿在iis中,寄宿在進程中的windows服務更加的穩定可靠(文章中的例子用windows控制台程式示範,後面給出完整的windows服務的代碼)。
2、前端部分:html5 + jQuery + bootstrap。基本的前端快速開發利器。
一、分析一下聊天室的情境需求,以便構建合適的資料結構。
1、線上使用者類 OnlineUser 使用者的基本特徵為姓名Name(性別年齡啥的先忽略),當然在系統設計中,姓名並不能很好的區分不同使用者,所以得需要一個唯一識別碼Id。另外,由於可能存在多個聊天室,因此聊天室的編號RoomId也是使用者的特徵之一。綜上,可得線上使用者類OnlineUser的結構為:
/// <summary>/// 線上使用者資訊/// <summary>public class OnlineUser{ public int Id { get; set; } public string Name { get; set; } public string RoomId { get; set; } public string SessionId { get; set; }}
PS:SessionId是後續加入的屬性,此處可先忽略。
2、訊息類 Message 訊息是聊天室最核心的部分,我最簡單的訊息機構應包含如下成員,訊息寄件者FromUserId,訊息接受者ToUserId,訊息類型Type,訊息內容Content,訊息時間Time。
public class Message{ public int FromUserId { get; set; } public int ToUserId { get; set; } public int Type { get; set; } public string Time { get; set; } public string Content { get; set; }}
PS:這裡Type用的是int類型表示的,是出於後續訊息傳遞時,以便於將其包裹成Json格式傳遞。但在判斷時,會將其轉換成MessageType格式的枚舉型,MessageType暫訂包含以下幾種狀態,
public enum MessageType{ /// <summary> /// 新使用者進入 /// <summary> NewUserIn = 1, /// <summary> /// 使用者離開 /// <summary> UserExit = 2, /// <summary> /// 新使用者提供自身資訊 /// <summary> ReprotUserInfo = 3, /// <summary> /// 新文字訊息 /// </summary> NewTextMessage = 4, /// <summary> /// 廣播基本資料 /// </summary> BroadcastBasicInfo = 5}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
二、項目結構
1、服務端項目結構如下:
這是一個windows控制台程式,Program.cs是入口,ChatWebSocket.cs是核心代碼快,外部參考的dll包括Json操作類庫和Socket操作類庫。
其中,Model檔案夾下的是上面提到的一些基本資料結構,這裡看一下核心的代碼ChatWebSocket。這當中用到的SuperSocket已經將socket的主要操作封裝的很完備了,使用方法如下:
/* 偵聽地址(注意,此處的地址一定要和前端js中的地址一致!!) */const string IP = "127.0.0.1";/* 偵聽連接埠 */const int PORT = 2016;/* SuperWebSocket中的WebSocketServer對象 */WebSocketServer wsServer = null;/* 當前線上使用者列表 */List<OnlineUser> olUserList = new List<OnlineUser>();/* 定時通知用戶端線程 */BackgroundWorker bkWork = null;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
這裡,WebSocketServer是SuperSocket中封裝好的Socket服務端類,olUserList 是線上使用者列表,bkWork 是後台線程,負責定時向用戶端發送一些系統資訊。
建構函式中:
public ChatWebSocket(){ /* 初始化 以及 相關事件註冊 */ wsServer = new WebSocketServer(); /* 有新會話握手並串連成功 */ wsServer.NewSessionConnected += WsServer_NewSessionConnected; /* 有會話被關閉 可能是服務端關閉 也可能是用戶端關閉 */ wsServer.SessionClosed += WsServer_SessionClosed; /* 有新簡訊被接收 */ wsServer.NewMessageReceived += WsServer_NewMessageReceived;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
WebSocketServer有幾個比較重要的事件,
1、NewSessionConnected 有新會話握手並串連成功
2、SessionClosed 有會話被關閉 可能是服務端關閉 也可能是用戶端關閉
3、NewMessageReceived 有新簡訊被接收
其中NewMessageReceived事件是訊息傳遞的重要事件,也是我們重點處理的事件,下面將3個事件的方法體列出:
// 有新會話握手並串連成功private void WsServer_NewSessionConnected(WebSocketSession session){ LogHelper.Write(session.SessionID.ToString() + " Connect!");}// 有新簡訊被接收private void WsServer_NewMessageReceived(WebSocketSession session, string value){ LogHelper.Write("Receive Message:" + value); var msg = JsonConvert.DeserializeObject<Message>(value); MessageType mt = (MessageType)msg.Type; switch (mt) { /* 使用者報告自己資訊,將UserId與SessionId關聯 */ case MessageType.ReprotUserInfo: olUserList.Add(new OnlineUser { SessionId = session.SessionID, Id = msg.FromUserId, RealName = msg.FromUserName, RoomId = msg.RoomId }); /* 通知其他使用者 */ SendMessage(session, new Message { FromUserId = msg.FromUserId, FromUserName = msg.FromUserName, ToUserId = 0,// 同一房間的人 Type = (int)MessageType.NewUserIn, Content = "", Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), RoomId = msg.RoomId }); break; /* 使用者文字(圖片)訊息,伺服器進行轉寄 */ case MessageType.NewTextMessage: /* 通知其他使用者 */ msg.Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); SendMessage(session, msg); break; default: break; }}// 有會話被關閉 可能是服務端關閉 也可能是用戶端關閉private void WsServer_SessionClosed(WebSocketSession session, CloseReason value){ LogHelper.Write(session.SessionID.ToString() + " Exit!"); var u = olUserList.Find(m => m.SessionId == session.SessionID); if (u == null) { return; } olUserList.Remove(u); // 通知其他使用者 SendMessage(session, new Message { FromUserId = u.Id, FromUserName = u.RealName, ToUserId = 0,// 同一房間的人 Type = (int)MessageType.UserExit, Content = "", RoomId = u.RoomId });}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
其中,SendMessage(WebSocketSession session, Message msg)
方法如下:
private void SendMessage(WebSocketSession session, Message msg){ // -1:全體;0:同一房間;剩下:特定的使用者 var users = msg.ToUserId == -1 ? olUserList : msg.ToUserId == 0 ? olUserList.Where(m => m.RoomId == msg.RoomId).ToList() : olUserList.Where(m => m.Id == msg.ToUserId).ToList(); users.ForEach(u => { var ss = session.AppServer.GetAppSessionByID(u.SessionId); if (ss != null) { ss.Send(JsonConvert.SerializeObject(msg)); } });}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
另外,WebSocketServer的啟動和停止也非常的簡單:
public void Start(){ if (!wsServer.Setup(IP, PORT)) { throw new Exception("設定WebSocket服務偵聽地址失敗!"); } if (!wsServer.Start()) { throw new Exception("啟動WebSocket服務偵聽失敗!"); }}public void Stop(){ if (wsServer != null) { wsServer.Stop(); }}
WebSocket 網頁聊天室的實現(伺服器端:.net + windows服務,前端:Html5)