一、NetCoreServer介绍
超快速、低延迟的异步套接字服务器和客户端 C# .NET Core 库,支持 TCP、SSL、UDP、HTTP、HTTPS、WebSocket 协议和 10K 连接问题解决方案。
开源地址:https://github.com/chronoxor/NetCoreServer
支持:
Example: TCP chat server
Example: TCP chat client
Example: SSL chat server
Example: SSL chat client
Example: UDP echo server
Example: UDP echo client
Example: UDP multicast server
Example: UDP multicast client
Example: Unix Domain Socket chat server
Example: Unix Domain Socket chat client
Example: Simple protocol
Example: Simple protocol server
Example: Simple protocol client
Example: HTTP server
Example: HTTP client
Example: HTTPS server
Example: HTTPS client
Example: WebSocket chat server
Example: WebSocket chat client
Example: WebSocket secure chat server
Example: WebSocket secure chat client
本文重点学习WebSocket通讯
二、服务端及双客户端代码
2.1 服务端控制台
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using NetCoreServer;namespace WsChatServer
{class ChatSession : WsSession{public ChatSession(WsServer server) : base(server) {}public override void OnWsConnected(HttpRequest request){Console.WriteLine($"Chat WebSocket session with Id {Id} connected!");// Send invite messagestring message = "Hello from WebSocket chat! Please send a message or '!' to disconnect the client!";SendTextAsync(message);}public override void OnWsDisconnected(){Console.WriteLine($"Chat WebSocket session with Id {Id} 已断开!");}public override void OnWsReceived(byte[] buffer, long offset, long size){string message = Encoding.UTF8.GetString(buffer, (int)offset, (int)size);Console.WriteLine("来自: " + message);// Multicast message to all connected sessions((WsServer)Server).MulticastText(message);// If the buffer starts with '!' the disconnect the current sessionif (message == "!")Close();}protected override void OnError(SocketError error){Console.WriteLine($"Chat WebSocket session caught an error with code {error}");}}class ChatServer : WsServer{public ChatServer(IPAddress address, int port) : base(address, port) {}protected override TcpSession CreateSession() { return new ChatSession(this); }protected override void OnError(SocketError error){Console.WriteLine($"错误 {error}");}}class Program{static void Main(string[] args){// WebSocket server port 服务端口int port = 9999;if (args.Length > 0)port = int.Parse(args[0]);// WebSocket server content pathstring www = "../../../../../www/ws";if (args.Length > 1)www = args[1];Console.WriteLine($"WebSocket 服务端口: {port}");Console.WriteLine($"WebSocket server static content path: {www}");Console.WriteLine($"WebSocket server website: http://localhost:{port}/chat/index.html");Console.WriteLine();// Create a new WebSocket servervar server = new ChatServer(IPAddress.Any, port);server.AddStaticContent(www, "/chat");// Start the serverConsole.Write("服务端启动...");server.Start();Console.WriteLine("完成!");Console.WriteLine("Press Enter to stop the server or '!' to restart the server...");// Perform text inputfor (;;){string line = Console.ReadLine();//接受输入if (string.IsNullOrEmpty(line))break;// Restart the serverif (line == "!"){//重启标识!Console.Write("Server restarting...");server.Restart();Console.WriteLine("Done!");}// Multicast admin message to all sessionsline = "[管理员] " + line; //前缀admin管理员消息server.MulticastText(line);//广播}// Stop the serverConsole.Write("服务端停止...");server.Stop();Console.WriteLine("完成!");}}
}
2.2 客户端html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><title>WebSocket聊天客户端示例</title><link rel="icon" type="image/png" href="./favicon.png"/>
</head>
<body><script>var myname;var isconned=false;//初始化function init(){document.myform.url.value = "ws://localhost:9999/"//不要和系统的端口冲突document.myform.inputtext.value = "Hello World!"//问候语document.myform.disconnectButton.disabled = true//断开连接默认禁用}
//打开连接function doConnect(){isconned=true;websocket = new WebSocket(document.myform.url.value)websocket.onopen = function(evt) { onOpen(evt) }websocket.onclose = function(evt) { onClose(evt) }websocket.onmessage = function(evt) { onMessage(evt) }websocket.onerror = function(evt) { onError(evt) }}
//打开function onOpen(evt){writeToScreen("connected\n")document.myform.connectButton.disabled = truedocument.myform.disconnectButton.disabled = falsemyname=document.myform.myname.value}
//关闭function onClose(evt){writeToScreen("disconnected\n")document.myform.connectButton.disabled = falsedocument.myform.disconnectButton.disabled = true}
//发消息function onMessage(evt){writeToScreen("接收<<" + evt.data + '\n')}
//错误function onError(evt){writeToScreen('error: ' + evt.data + '\n')websocket.close()//关闭websocketdocument.myform.connectButton.disabled = false//启用连接document.myform.disconnectButton.disabled = true//断开禁用}
//发送消息function doSend(message){writeToScreen("发送>>" + message + '\n')//回显websocket.send(message)//发送到服务端}
//输出到屏幕function writeToScreen(message){document.myform.outputtext.value += message//拼接document.myform.outputtext.scrollTop = document.myform.outputtext.scrollHeight//滚动到底部}
//监听window.addEventListener("load", init, false)
//发送方法function sendText(){var msg=document.myform.inputtext.value;if(msg==""||isconned==false){alert("对不起,请输入内容或先连接");return;}doSend("["+myname+"] "+msg)//消息内容}
//清屏function clearText(){document.myform.outputtext.value = ""}
//断开连接function doDisconnect(){isconned=false;websocket.close()}</script><h3>WebSocket Client</h3><form name="myform"><p><li>姓名:<input name="myname" value="X5ZJ" class="txt" /><li><li>地址:<input name="url" class="txt"/></li><li><input type="button" name=connectButton value="连接" onClick="doConnect()"><input type="button" name=disconnectButton value="断开" onClick="doDisconnect()"></li></p><p><textarea name="outputtext" cols="35" rows="10" readonly>聊天记录</textarea></p><p>内容:<input name="inputtext" class="txt"/></p><p><input type="button" name=sendButton value="发送" onClick="sendText()"><input type="button" name=clearButton value="清屏" onClick="clearText()"></p></form><style>p{line-height:20px;padding:4px}.txt{width:200px}li{list-style-type:none;margin:2px}</style>
</body>
</html>
2.3 客户端控制台
using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using NetCoreServer;namespace WsChatClient
{class ChatClient : WsClient{public ChatClient(string address, int port) : base(address, port) {}public void DisconnectAndStop(){_stop = true;CloseAsync(1000);while (IsConnected)Thread.Yield();}public override void OnWsConnecting(HttpRequest request){request.SetBegin("GET", "/");request.SetHeader("Host", "localhost");request.SetHeader("Origin", "http://localhost");request.SetHeader("Upgrade", "websocket");request.SetHeader("Connection", "Upgrade");request.SetHeader("Sec-WebSocket-Key", Convert.ToBase64String(WsNonce));request.SetHeader("Sec-WebSocket-Protocol", "chat, superchat");request.SetHeader("Sec-WebSocket-Version", "13");request.SetBody();}public override void OnWsConnected(HttpResponse response){Console.WriteLine($"Chat WebSocket client connected a new session with Id {Id}");}public override void OnWsDisconnected(){Console.WriteLine($"Chat WebSocket client disconnected a session with Id {Id}");}public override void OnWsReceived(byte[] buffer, long offset, long size){Console.WriteLine($"Incoming: {Encoding.UTF8.GetString(buffer, (int)offset, (int)size)}");}protected override void OnDisconnected(){base.OnDisconnected();Console.WriteLine($"Chat WebSocket client disconnected a session with Id {Id}");// Wait for a while...Thread.Sleep(1000);// Try to connect againif (!_stop)ConnectAsync();}protected override void OnError(SocketError error){Console.WriteLine($"Chat WebSocket client caught an error with code {error}");}private bool _stop;}class Program{static void Main(string[] args){// WebSocket server addressstring address = "127.0.0.1";if (args.Length > 0)address = args[0];// WebSocket server port 服务端口一致 9999int port = 9999;if (args.Length > 1)port = int.Parse(args[1]);Console.WriteLine($"WebSocket server address: {address}");Console.WriteLine($"WebSocket server port: {port}");Console.WriteLine();// Create a new TCP chat clientvar client = new ChatClient(address, port);// Connect the clientConsole.Write("Client connecting...");client.ConnectAsync();//连接Console.WriteLine("Done!");Console.WriteLine("Press Enter to stop the client or '!' to reconnect the client...");//获取Guid值作为随机数种子string guid = System.Guid.NewGuid().ToString();Random random = new Random(guid.GetHashCode());string IdPart = random.Next(10000, 99999).ToString();// Perform text inputfor (;;){string line = Console.ReadLine();//接受输入if (string.IsNullOrEmpty(line))break;// Disconnect the clientif (line == "!"){//端口连接符!Console.Write(IdPart+"Client disconnecting...");client.SendTextAsync(IdPart+",will be disconnecting...");client.DisconnectAsync();Console.WriteLine("Done!");continue;}// Send the entered text to the chat server 发送内容client.SendTextAsync(string.Format("[{0}] {1} ({2})", IdPart,line,DateTime.Now.ToString("MM-dd HH:mm:ss FFF")));//客户端发送}// Disconnect the clientConsole.Write("Client disconnecting...");client.DisconnectAndStop();//断开Console.WriteLine("Done!");}}
}
客户端可以继续优化实现群聊。注意服务端口和ws地址改成服务器地址即可。