【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原理、应用和代码的讲解

相关推荐
优梦创客5 小时前
《黑神话悟空》开发框架与战斗系统解析
unity·游戏开发·黑神话悟空·战斗系统·解包
营赢盈英8 小时前
Azure OpenAI and token limit
ai·chatgpt·asp.net·azure·openai api
仙魁XAN11 小时前
Unity 设计模式 之 创造型模式-【工厂方法模式】【抽象工厂模式】
unity·设计模式·工厂方法模式·抽象工厂模式
躺下睡觉~21 小时前
Unity-Transform类-父子关系
java·unity·游戏引擎
Java资深爱好者21 小时前
VB.NET中如何利用ASP.NET进行Web开发
前端·asp.net·.net
躺下睡觉~21 小时前
Unity-Transform类-缩放和看向
unity·游戏引擎
superconvert1 天前
主流流媒体的综合性能大 PK ( smart_rtmpd, srs, zlm, nginx rtmp )
websocket·ffmpeg·webrtc·hevc·rtmp·h264·hls·dash·rtsp·srt·flv
君莫愁。1 天前
【Unity】检测鼠标点击位置是否有2D对象
unity·c#·游戏引擎
咩咩觉主1 天前
Unity实战案例全解析:PVZ 植物卡片状态分析
unity·c#·游戏引擎
蓝裕安1 天前
伪工厂模式制造敌人
开发语言·unity·游戏引擎