【Unity程序】和【控制台程序】连接【asp.net core的websocket服务】(1)——在编辑器中运行

一、说明

1、本文实验内容所涉及的开发环境说明:

  • win11
  • VisualStudio2022(.Net6.0)
  • Unity2021.3.40

2、本文参考资料

【1】NativeWebSocketUnity包:

https://github.com/endel/NativeWebSocket

【2】asp.net core架设websocket国外小哥案例:

https://medium.com/bina-nusantara-it-division/implementing-websocket-client-and-server-on-asp-net-core-6-0-c-4fbda11dbceb

【3】感谢自动纺织机:ChatGPT/Claude

二、本文要实现的测试内容

  • 1、实现一个ASP.NET CORE服务端,它上面跑着一个websocket服务
  • 2、在【控制台应用程序】中与服务器websocket通信
  • 3、在【Unity程序】中与服务器websocket通信

三、测试效果截图

1、服务端截图

有一个用户连入时,显示简单信息

2、控制台应用程序截图

连接成功后,提示。

服务器一旦有用户连入,广播用户信息,广播总用户数

3、Unity程序截图

连接成功后,提示。

服务器一旦有用户连入,广播用户信息,广播总用户数

四、服务端创建

1、打开vs2022 -> 【创建新项目】

2、选择【ASP.NET CORE 空】项目模版

3、配置新项目

设置完毕点击【下一步】

4、 其他信息

譬如设置框架

设置完毕,点击【创建】

5、编写服务端逻辑

服务端代码

csharp 复制代码
using System.Diagnostics;
using System.Net;
using System.Net.WebSockets;
using System.Text;

var builder = WebApplication.CreateBuilder(args); //
var connections = new List<WebSocket>();   //连接对象列表
										   //builder.WebHost.UseUrls("http://localhost:6969");
builder.WebHost.UseUrls("http://192.168.0.173:6969");

var app = builder.Build();                 //构建web应用程序实例
app.UseWebSockets();                       //启用websocket支持

app.Map("/ws", async context =>            //app.Map() 把某类网络请求与某个处理函数关联起来:当用户请求的是【/ws】时,服务器响应后面的兰姆达方法
{
	/**************************
	 每个ws连接请求发生的时候,服务器都会调用该处理流程 
	 这是一个并发流程,每一个ws请求都会经过它的处理
	 **************************/
	if (context.WebSockets.IsWebSocketRequest)
	{
		var curName = context.Request.Query["id"];//有用户连入
		var id = context.Connection.Id;
		var ip = context.Connection.RemoteIpAddress;
		Debug.WriteLine($"有用户连入:用户名为[{curName}]【Id= [{context.Connection.Id}] ip = [{context.Connection.RemoteIpAddress}]】");

		using var ws = await context.WebSockets.AcceptWebSocketAsync();
		connections.Add(ws);                      //添加连接的用户

		//广播信息
		await Broadcast($"有用户连入:id = [{id}],ip = [{ip}]");
		await Broadcast($"当前连接数量:{connections.Count}");
		await ReceiveMessage(ws,
			async (result, buffer) =>
			{
				if (result.MessageType == WebSocketMessageType.Text) //UTF8 文本信息
				{
					string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
					await Broadcast(ip + ":" + message);
				}
				else if (result.MessageType == WebSocketMessageType.Close || ws.State == WebSocketState.Aborted) //连接关闭
				{
					connections.Remove(ws);
					await Broadcast($"{ip}-{id}用户掉线了");
					await Broadcast($"当前用户连接数为{connections.Count}");
					await ws.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
				}
			});
	}
	else
	{
		context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
	}
});
await app.RunAsync();


//接收消息
async Task ReceiveMessage(WebSocket socket, Action<WebSocketReceiveResult, byte[]> handleMessage)
{
	var buffer = new byte[4096];
	while (socket.State == WebSocketState.Open)
	{
		var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
		handleMessage(result, buffer);
	}
}

//广播消息
async Task Broadcast(string message)
{
	var bytes = Encoding.UTF8.GetBytes(message);
	foreach (var socket in connections)
	{
		if (socket.State == WebSocketState.Open)
		{
			var arraySegment = new ArraySegment<byte>(bytes, 0, bytes.Length);
			await socket.SendAsync(arraySegment, WebSocketMessageType.Text, true, CancellationToken.None);
		}
	}
}

五、控制台客户端创建

1、创建新项目

2、选择【C#控制台应用】

3、配置新项目

4、设置.NET框架

5、编辑客户端逻辑

完整代码

csharp 复制代码
// See https://aka.ms/new-console-template for more information

using System.Net.WebSockets;
using System.Text;

string name;
while (true)
{
	Console.Write("请输入名字:");
	name = Console.ReadLine();
	break;
}


var ws = new ClientWebSocket();

Console.WriteLine("开始连接服务器!");
//await ws.ConnectAsync(new Uri("ws://localhost:6969/ws"),CancellationToken.None);
await ws.ConnectAsync(new Uri("ws://192.168.0.173:6969/ws"), CancellationToken.None);

Console.WriteLine("连接成功!");

var receiveTask = Task.Run(async () => 
{
	var buffer = new byte[1024];
    while (true)
	{
		var result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer),CancellationToken.None);
		if(result.MessageType == WebSocketMessageType.Close) 
		{  
			break; 
		}
		
		var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
		Console.WriteLine("收到信息:" + message);
	}
});

var sendTask = Task.Run(async () => 
{
	while (true)
	{
		var message =Console.ReadLine();
		if(message == "exit")
		{
			break;
		}

		var bytes = Encoding.UTF8.GetBytes(message);
		await ws.SendAsync(new ArraySegment<byte>(bytes),WebSocketMessageType.Text,true,CancellationToken.None);
	}
});

//发送或接收
await Task.WhenAny(receiveTask, sendTask);
if (ws.State == WebSocketState.Closed)
{
	await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
}

//发送和接收
await Task.WhenAll(receiveTask, sendTask);

六、Unity客户端创建

1、UI设计

2、层级关系

3、行为脚本清单

csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
using NativeWebSocket;
using System;
using Cysharp.Threading.Tasks;

public class ConnectTest : MonoBehaviour
{
    /// <summary>
    /// IP地址
    /// </summary>
    public TMP_InputField ipf_ip;
    
    /// <summary>
    /// 端口号
    /// </summary>
    public TMP_InputField ipf_port;

    /// <summary>
    /// 连接服务器btn
    /// </summary>
    public Button btn_connect;

    /// <summary>
    /// 提示信息
    /// </summary>
    public TMP_Text text_info;

	private WebSocket websocket;

    // Start is called before the first frame update
    void Start()
    {
        //连接服务器-按钮-逻辑
        btn_connect.onClick.AddListener(async () =>
        {
            var ip = ipf_ip.text;
            var port = ipf_port.text;			

			try
			{
				websocket = new WebSocket($"ws://{ip}:{port}/ws");
				Debug.Log($"开始连接服务------ws://{ip}:{port}/ws");
				text_info.text += $"\n 开始连接服务------ws://{ip}:{port}/ws  \n";

				//连接打开时...
				websocket.OnOpen += () =>
				{
					text_info.text += "连上服务器\n";
				};

				//连接出错时...
				websocket.OnError += (e) =>
				{
					text_info.text += $"连接出错:{e}\n";
				};

				//连接关闭时...
				websocket.OnClose += (e) =>
				{
					text_info.text += $"连接关闭:{e}\n";
				};

				//收到二进制数据时...
				websocket.OnMessage += (bytes) =>
				{
					var message = System.Text.Encoding.UTF8.GetString(bytes);					
					text_info.text += $"收到消息: {message} \n";
				};

				//开始连接
				await websocket.Connect();				
			}
			catch (Exception e)
			{
				Debug.Log($"连接服务器出错:{e.Message}");
				text_info.text = $"连接服务器出错:{e.Message}";				
			}
		});
	}

	void Update()
	{
#if !UNITY_WEBGL || UNITY_EDITOR
		websocket.DispatchMessageQueue();
#endif
	}

}

至此,全部菜上齐!

后面会进行ASP.NET CORE 、websocket原理、应用和代码的讲解

相关推荐
张老师带你学16 小时前
unity TerrainSampleAssets
科技·游戏·unity·游戏引擎·模型
RReality19 小时前
【Unity Shader URP】色带渐变着色(Ramp Shading)实战教程
ui·unity·游戏引擎·图形渲染
mxwin1 天前
Unity URP 体积光与雾效 基于深度重建世界空间位置,实现体积雾与体积光
unity·游戏引擎
张老师带你学1 天前
unity 树资源 有樱花树 buildin
科技·游戏·unity·游戏引擎·模型
魔士于安1 天前
unity 植物 不常见 花 触手植物
游戏·unity·游戏引擎·贴图·模型
魔士于安1 天前
unity=>传送门特效 带自由视角旋转放大 鼠标操作
前端·游戏·unity·游戏引擎·贴图·模型
南無忘码至尊1 天前
Unity学习90天 - 第4天 - 认识物理系统基础并实现物体碰撞反弹
学习·unity·游戏引擎
南無忘码至尊1 天前
Unity学习90天 - 第4天 - 学习预制体 Prefab + 实例化并实现按鼠标生成子弹
学习·unity·游戏引擎
指针刺客1 天前
网络协议之WebSocket
网络·websocket·网络协议
大G的笔记本1 天前
Java WebSocket客户端--java.net.http.HttpClient
java·websocket·.net