1, Core description of the project
1. Fleck is a relatively simple third-party component to implement websocket, which does not need to install additional containers. There are also several interfaces available.
2. The project is based on. net framework 4.7.2, developed on vs2019, and has not tried to run on the lower version. However, the new features are not used in the code, so it is estimated that they can be used by the lower version.
3. This project is not a real project, i.e. play it, but it should be a little enlightening to be familiar with Fleck or know how to chat in time.
2, Brief description of fleck( https://github.com/statianzo/Fleck )
1. The simplest and most commonly used call method: (ws://172.10.3.4:8111 change to your server local IP and port)
//Console code var server = new WebSocketServer("ws://172.10.3.4:8111"); server.Start(socket => { socket.OnOpen = () => Console.WriteLine("Generate connection processing"); socket.OnClose = () => Console.WriteLine("Disconnect processing"); socket.OnMessage = (message) => { //1,This method is used to receive messages from clients //2,You can do some of your own operations, such as storing in a database //3,In response to the client, the following send Function to return the response result. socket.Send(message); } });
2. Fleck is only responsible for the single line connection. That is to say, when client A establishes A connection with the server, an IWebSocketConnection will be generated, which is the type of socket variable in the above code. It includes the receiving method and sending method, but they are only limited to A single connection. As for client A who wants to send messages to client B, C, D or want to send them in groups, I'm sorry that fleck itself doesn't Care... Of course, that's not the function that can't be realized. It's just for developers to store every IWebSocketConnection and manage their life cycle. Through their own code, client A can send information to B or group send information to B.
3. Fleck does not need additional containers or processes to run. It runs with the IIS website, that is, in W3 wp.exe . As for how it works, I haven't looked at the source code yet. I'll have a look later.
3, Chat source location
1,GitHub: https://github.com/DisSunRestart2020/DisSunChat
2, Code cloud: https://gitee.com/dissun/DisSunChat
3. Wechat code scanning demonstration (good or bad network time)
4, Core code description.
1. IWebSocketHelper interface. At the beginning, I wanted to use multiple plug-ins to implement chat room, so I wanted to use one interface to do behavior encapsulation. As a result, after Fleck is completed, it is found that other operation modes are not the same, so it is difficult to encapsulate and give up. However, this interface is reserved to reflect the scalability.
/// <summary> /// All calls WebSocket The protocol that must be followed by the help class of /// </summary> public interface IWebSocketHelper { /// <summary> /// websocket Event triggered after connection /// </summary> event SwitchEventHandler WsOpenEventHandler; /// <summary> /// websocket Event triggered after connection is closed /// </summary> event SwitchEventHandler WsCloseEventHandler; /// <summary> /// websocket Event triggered after listening to a message /// </summary> event ListenEventHandler WsListenEventHandler; /// <summary> /// websocket Response processing events /// </summary> event ResponseTextEventHandler WsResponseTextEventHandler; /// <summary> /// Number of chat rooms online /// </summary> int PlayerCount { get; set; } /// <summary> /// websocket initialization /// </summary> void WebSocketInit(); /// <summary> /// Send information to all /// </summary> /// <param name="wsocketMsg"></param> void SendMessageToAll(WebSocketMessage wsocketMsg); /// <summary> /// Send information to yourself /// </summary> /// <param name="wsocketMsg"></param> void SendMessageToMe(WebSocketMessage wsocketMsg); }
Four delegate events are used in the interface. Here is a brief review of delegates and events.
① In terms of use form, delegation means that we can transfer a function as a parameter. For example, we are solving a mathematical problem with the same conditions and requirements, but the process of solution can be varied. Through delegation, we can encapsulate different solution processes into different functions without changing the main program, and then transfer the function as a parameter Enter the main program.
② From the perspective of pattern, delegation is to implement observer pattern. The subscriber / observer tells the publisher / topic what to do if a specific event occurs, and the process of "what to do" is the content of the delegation method
③ Event is actually a delegation. It is said that event is a special delegation. What's special is that it adds constraints to the delegation, so you can't use the delegation without any scruples. This is to ensure encapsulation.
④ In fact, I can directly replace the above four events with four delegation properties, which will not have a great impact on the program operation. However, the third point is encapsulation. The original intention of the event is to let the publisher trigger the execution of the delegate method after the specific conditions are met. However, if the delegate property is used, the subscriber can call the event itself, and the encapsulation is poor.
⑤ The above four events are that the subscriber tells the Fleck center what to do when the new long connection is connected, what to do when the long connection is disconnected, what to do when the client sends a message, and how to convert the message to be returned to the client.
2. Implementation of fleck class. Fleck implements the IWebSocketHelper interface, which is the core code of this project.
1 public class FleckHelper:IWebSocketHelper 2 { 3 /// <summary> 4 /// websocket Event triggered after connection 5 /// </summary> 6 public event SwitchEventHandler WsOpenEventHandler; 7 /// <summary> 8 /// websocket Event triggered after connection is closed 9 /// </summary> 10 public event SwitchEventHandler WsCloseEventHandler; 11 /// <summary> 12 /// websocket Event triggered after listening to a message 13 /// </summary> 14 public event ListenEventHandler WsListenEventHandler; 15 /// <summary> 16 /// websocket Feedback client's text processing events 17 /// </summary> 18 public event ResponseTextEventHandler WsResponseTextEventHandler; 19 /// <summary> 20 /// Number of chat rooms online 21 /// </summary> 22 public int PlayerCount 23 { 24 get; 25 set; 26 } 27 /// <summary> 28 /// websocket Connected connection set 29 /// </summary> 30 private Hashtable socketListHs = new Hashtable(); 31 32 public void WebSocketInit() 33 { 34 35 string websocketPath = Utils.GetConfig("websocketPath"); 36 WebSocketServer wsServer = new WebSocketServer(websocketPath); 37 38 wsServer.Start(socket => 39 { 40 //The following settings will take effect whenever a new connection comes in. 41 socket.OnOpen = () => { 42 //Custom processing 43 44 if (this.WsOpenEventHandler != null) 45 { 46 WebsocketEventArgs args = new WebsocketEventArgs(); 47 this.WsOpenEventHandler(this, args); 48 } 49 }; 50 51 socket.OnClose = () => { 52 //Remove from connection collection 53 for (int i= socketListHs.Count-1; i>=0;i--) 54 { 55 if (socketListHs[i] == null) 56 { 57 socketListHs.Remove(i); 58 } 59 } 60 PlayerCount = socketListHs.Count; 61 //Custom processing 62 if (this.WsCloseEventHandler != null) 63 { 64 WebsocketEventArgs args = new WebsocketEventArgs(); 65 this.WsCloseEventHandler(this, args); 66 } 67 }; 68 69 socket.OnMessage = (message) => 70 { 71 ClientData cData = Utils.JsonToObject<ClientData>(message); 72 WebSocketMessage wsocketMsg = new WebSocketMessage(socket.ConnectionInfo.ClientIpAddress, socket.ConnectionInfo.ClientPort.ToString(), socket.ConnectionInfo.Id.ToString("N"), cData); 73 74 if (Convert.ToBoolean(cData.IsConnSign)) 75 { 76 //Receive user online information and update socket list 77 if (!socketListHs.ContainsKey(cData.IdentityMd5)) 78 { 79 socketListHs.Add(cData.IdentityMd5, socket); 80 } 81 else 82 { 83 socketListHs[cData.IdentityMd5] = socket; 84 } 85 PlayerCount = socketListHs.Count; 86 } 87 88 if (this.WsListenEventHandler != null) 89 { 90 WebsocketEventArgs args = new WebsocketEventArgs(); 91 args.WebSocketMessage = wsocketMsg; 92 this.WsListenEventHandler(this, args); 93 } 94 }; 95 96 }); 97 } 98 99 /// <summary> 100 /// Send information to all 101 /// </summary> 102 /// <param name="wsocketMsg"></param> 103 public void SendMessageToAll(WebSocketMessage wsocketMsg) 104 { 105 string resultData = ""; 106 if (this.WsResponseTextEventHandler != null) 107 { 108 WebsocketEventArgs args = new WebsocketEventArgs(); 109 args.WebSocketMessage = wsocketMsg; 110 this.WsResponseTextEventHandler(this, args); 111 resultData = args.ResultDataMsg; 112 } 113 114 if (!string.IsNullOrWhiteSpace(resultData)) 115 { 116 foreach (DictionaryEntry dey in socketListHs) 117 { 118 IWebSocketConnection subConn = (IWebSocketConnection)dey.Value; 119 subConn.Send(resultData); 120 } 121 } 122 } 123 124 }
① In the code, IdentityMd5 is the identity calculated by myself through various local information. Because the cookie running on wechat is not stable, no information can be stored locally, and only the environment information can be synthesized into a unique value by algorithm.
② As mentioned earlier, Fleck is only responsible for single line connection, and each connection is an IWebSocketConnection, so I need to save IdentityMd5 and IWebSocketConnection for index calling.
3. socket.OnMessage In, socketListHs is a collection of iwebsocketconnections. Every time a message is sent, if it is judged to be a new IdentityMd5, it will be saved.
4. socket.OnClose If any connection is broken, IWebSocketConnection will be null, so traverse the collection, and then remove the null value to maintain the validity of socketListHs collection
⑤ SendMessageToAll function is used to send messages in groups, traverse all objects in the collection, and call each IWebSocketConnection.Send(), you can send the message. If you want to send from client A to client B, you have to use IdentityMd5 to do the article. This project is omitted because the chat room does not need this function.
6. socket.OnOpen , socket.OnClose , socket.OnMessage It's all events triggered by Fleck itself. We introduced it at the beginning. For example, in onopen, if we need to record logs when establishing a new connection, we can assign a value to WsOpenEventHandler (this project is in Global.asax Application of_ Start is assigned with lambda), as follows:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RouteConfig.RegisterRoutes(RouteTable.Routes); App_Start.FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); ChatService chatService = new ChatService(); try { IWebSocketHelper helper = new FleckHelper();//use FleckHelper To instantiate IWebSocketHelper Interface. helper.WebSocketInit(); helper.WsOpenEventHandler +=(sender, args)=>{ Utils.SaveLog("WebSocket Opened 2"); }; helper.WsCloseEventHandler += (sender, args) => { Utils.SaveLog("WebSocket Closed 2"); }; helper.WsListenEventHandler += (sender, args) => { Utils.SaveLog("WebSocket Message 2 detected"); if (!Convert.ToBoolean(args.WebSocketMessage.ClientData.IsConnSign)) { chatService.CreateChatInfo(args.WebSocketMessage); args.WebSocketMessage.ClientData.SMsg = Utils.ReplaceIllegalWord(args.WebSocketMessage.ClientData.SMsg); } else { string clientName = args.WebSocketMessage.CIp + ":" + args.WebSocketMessage.CPort; string traceInfo = string.Format("{0} Join chat(common{1}People online)", clientName, helper.PlayerCount); args.WebSocketMessage.ClientData.SMsg = traceInfo; } //Forward messages to everyone helper.SendMessageToAll(args.WebSocketMessage); }; helper.WsResponseTextEventHandler += (sender, args) => { string jsonStr = Utils.ObjectToJsonStr(args.WebSocketMessage); args.ResultDataMsg = jsonStr; }; } catch(Exception ex) { Utils.SaveLog("Errors found:" + ex.Message); } }
3. To connect Fleck on the html side, only one needs to be connected WebSocketJs.js Documents.
var socket; var websocketInit = function (wsPath) { if (typeof (WebSocket) === "undefined") { socket = null; console.log("Browser does not support websocket"); } else { // instantiation socket socket = new WebSocket(wsPath); // monitor socket connect socket.onopen = wsOnOpen; //monitor socket close socket.onclose = wsOnClose; // monitor socket error message socket.onerror = wsOnError; // monitor socket news socket.onmessage = wsOnMessage; } } var wsOnOpen = function () { console.log("Successfully connected"); var sMsg = ""; var sendMsg = "{\"IdentityMd5\":\"" + identityMd5 + "\",\"SMsg\":\"\",\"ImgIndex\":\"" + imgIndex + "\",\"IsConnSign\":\"true\"}"; socket.send(sendMsg); } var wsOnClose = function () { console.log("Connection closed"); } var wsOnError = function (evt) { console.log("abnormal:" + evt); } var wsSend = function (sMsg) { if (socket == null || sMsg == "") return false; console.log("Message sent successfully"); socket.send(sMsg); }
① WebSocket is a built-in type of browser, which is now supported by general browsers, but it is still the type of (WebSocket) to make a judgment.
② Then load it at startup (change ws://14.215.177.1:8111 to your server public IP and port).
$(function () { websocketInit("ws://14.215.177.1:8111"); });
********** End *****************
These are only part of the code above. You need to run the code. Please go to github to download the source code.
At present, the company's development is not smooth, the old projects are suspended, and the demand for new projects is unclear. I'm nervous about the current trend of enterprise bankruptcy. After all, I'm not young, and I'm not particularly good at technology. I can't help worrying. But I also know that to act and get closer to my goal every day is the only way to solve anxiety.
If you can write some blogs, just write some blogs. I'm not sure how much it will help me or my readers. Just be a memo.
When a tossing "front wave"
We welcome constructive criticism and guidance.