一、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 message
string 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 session
if (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 path
string 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 server
var server = new ChatServer(IPAddress.Any, port);
server.AddStaticContent(www, "/chat");
// Start the server
Console.Write("服务端启动...");
server.Start();
Console.WriteLine("完成!");
Console.WriteLine("Press Enter to stop the server or '!' to restart the server...");
// Perform text input
for (;;)
{
string line = Console.ReadLine();//接受输入
if (string.IsNullOrEmpty(line))
break;
// Restart the server
if (line == "!")
{//重启标识!
Console.Write("Server restarting...");
server.Restart();
Console.WriteLine("Done!");
}
// Multicast admin message to all sessions
line = "[管理员] " + line; //前缀admin管理员消息
server.MulticastText(line);//广播
}
// Stop the server
Console.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 = true
document.myform.disconnectButton.disabled = false
myname=document.myform.myname.value
}
//关闭
function onClose(evt)
{
writeToScreen("disconnected\n")
document.myform.connectButton.disabled = false
document.myform.disconnectButton.disabled = true
}
//发消息
function onMessage(evt)
{
writeToScreen("接收<<" + evt.data + '\n')
}
//错误
function onError(evt)
{
writeToScreen('error: ' + evt.data + '\n')
websocket.close()//关闭websocket
document.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 again
if (!_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 address
string address = "127.0.0.1";
if (args.Length > 0)
address = args[0];
// WebSocket server port 服务端口一致 9999
int 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 client
var client = new ChatClient(address, port);
// Connect the client
Console.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 input
for (;;)
{
string line = Console.ReadLine();//接受输入
if (string.IsNullOrEmpty(line))
break;
// Disconnect the client
if (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 client
Console.Write("Client disconnecting...");
client.DisconnectAndStop();//断开
Console.WriteLine("Done!");
}
}
}
客户端可以继续优化实现群聊。注意服务端口和ws地址改成服务器地址即可。