unity socket 客户端和c#服务器通信

news2025/4/18 17:12:02

服务器

using BarrageGrab;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Lyx {


        class Server
        {
            private TcpListener listener;
            private ConcurrentDictionary<TcpClient, DateTime> clients = new ConcurrentDictionary<TcpClient, DateTime>();
            private CancellationTokenSource cts = new CancellationTokenSource();

            public void Start(int port)
            {
                listener = new TcpListener(IPAddress.Any, port);
                listener.Start();
                Console.WriteLine($"服务器已启动,监听端口 {port}...");

                new Thread(AcceptClients) { IsBackground = true }.Start();
                new Thread(CheckHeartbeats) { IsBackground = true }.Start();
                new Thread(HandleConsoleInput) { IsBackground = true }.Start();
            }

            private void AcceptClients()
            {
                try
                {
                    while (!cts.Token.IsCancellationRequested)
                    {
                        TcpClient client = listener.AcceptTcpClient();
                        Console.WriteLine("新客户端连接:" + client.Client.RemoteEndPoint);
                        clients.TryAdd(client, DateTime.Now);

                        new Thread(() => HandleClient(client)) { IsBackground = true }.Start();
                    }
                }
                catch (SocketException) { } // 监听器被停止时正常退出
            }

            private void HandleClient(TcpClient client)
            {
                NetworkStream stream = null;
                try
                {
                    stream = client.GetStream();
                    byte[] buffer = new byte[1024];

                    while (!cts.Token.IsCancellationRequested)
                    {
                        int bytesRead;
                        lock (client) // 同步网络流访问
                        {
                            if (!stream.DataAvailable)
                            {
                                Thread.Sleep(100);
                                continue;
                            }
                            bytesRead = stream.Read(buffer, 0, buffer.Length);
                        }

                        if (bytesRead <= 0) break;

                        string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                        Console.WriteLine($"收到消息: {message}");

                        if (message == "heartbeat")
                        {
                             clients.AddOrUpdate(client, DateTime.Now, (_, __) => DateTime.Now);
                             SendMessage(client, "heartbeat");
                        Logger.PrintColor($"服务器发送心跳: {message} ,给客户端成功:{client.Client.RemoteEndPoint}", ConsoleColor.Yellow);
                     
                        continue;
                        }

                        //SendMessage(client, "服务器收到: " + message);
                    }
                }
                catch (Exception ex) { Console.WriteLine($"客户端错误: {ex.Message}"); }
                finally
                {
                    if (client.Connected )
                    {
                        Console.WriteLine("客户端断开:" + client.Client.RemoteEndPoint);
                        clients.TryRemove(client, out _);
                        client.Close();
                        stream?.Close();
                    }
                    
                }
            }

            public void BroadcastMessage(string message)
            {
                byte[] data = Encoding.UTF8.GetBytes("服务器广播: " + message);
                foreach (var client in clients.Keys.ToArray())
                {
                    try
                    {
                        lock (client)
                        {
                            client.GetStream().Write(data, 0, data.Length);
                        }
                    }
                    catch { }
                }
                Console.WriteLine("广播消息已发送");
            }

            public void SendMessage(TcpClient client, string message)
            {
                if (!clients.ContainsKey(client)) return;

                byte[] data = Encoding.UTF8.GetBytes(message);
                try
                {
                    lock (client) // 同步网络流访问
                    {
                        client.GetStream().Write(data, 0, data.Length);
                    }
                    Console.WriteLine($"消息发送成功 -> {client.Client.RemoteEndPoint}: {message}");
                }
                catch
                {
                    Console.WriteLine("消息发送失败");
                }
            }

            private void HandleConsoleInput()
            {
                while (!cts.Token.IsCancellationRequested)
                {
                    string input = Console.ReadLine();
                    if (input?.ToLower() == "exit")
                    {
                        Stop();
                        break;
                    }
                    else if (!string.IsNullOrWhiteSpace(input))
                    {
                        BroadcastMessage(input);
                    }
                }
            }

            private void CheckHeartbeats()
            {
                while (!cts.Token.IsCancellationRequested)
                {
                    Thread.Sleep(5000);
                    DateTime now = DateTime.Now;

                    foreach (var client in clients.Keys.ToArray())
                    {
                        if (clients.TryGetValue(client, out DateTime last) &&
                           (now - last).TotalSeconds > 10)
                        {
                            Console.WriteLine("客户端超时断开:" + client.Client.RemoteEndPoint);
                            clients.TryRemove(client, out _);
                            client.Close();
                        }
                    }
                }
            }

            public void Stop()
            {
                cts.Cancel();
                listener.Stop();

                foreach (var client in clients.Keys.ToArray())
                {
                    client.Close();
                    clients.TryRemove(client, out _);
                }

                Console.WriteLine("服务器已停止");
            }

            public  void Main()
            {
                Server server = new Server();
                server.Start(54621);

           
            }
        }
    
}

客户端

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;

public class SocketClient : MonoBehaviour
{
    private TcpClient client;
    private NetworkStream stream;
    private Thread receiveThread;
    private bool isConnected = false;
    private string serverIP = "127.0.0.1"; // 服务器IP地址
    private int serverPort = 54621; // 服务器端口
    private float heartbeatTimeout = 5f; // 心跳超时时间
    private float heartbeatInterval = 0.5f; // 心跳发送间隔时间
    private float lastHeartbeatTime;
    private float lastSendHeartbeatTime;
    private bool updateHeartbeat = false; // 标志位,用于通知主线程更新心跳时间
    private int reconnectAttempts = 0; // 重连尝试次数
    private const int maxReconnectAttempts = 3; // 最大重连次数
    private const float reconnectInterval = 2f; // 重连间隔时间

    void Start()
    {
        ConnectToServer();
    }

    void Update()
    {
        // 检查是否需要更新心跳时间
        if (updateHeartbeat)
        {
            lastHeartbeatTime = Time.time;
            updateHeartbeat = false;
        }

        // 检查心跳超时
        if (isConnected && Time.time - lastHeartbeatTime > heartbeatTimeout)
        {
            Debug.Log("Heartbeat timeout, disconnecting...");
            Disconnect();
        }

        // 定时发送心跳消息
        if (isConnected && Time.time - lastSendHeartbeatTime > heartbeatInterval)
        {
            SendData("heartbeat");
            lastSendHeartbeatTime = Time.time;
        }
    }

    void ConnectToServer()
    {
        try
        {
            client = new TcpClient(serverIP, serverPort);
            stream = client.GetStream();
            isConnected = true;
            lastHeartbeatTime = Time.time;
            lastSendHeartbeatTime = Time.time;
            reconnectAttempts = 0; // 重置重连尝试次数

            receiveThread = new Thread(new ThreadStart(ReceiveData));
            receiveThread.IsBackground = true;
            receiveThread.Start();

            Debug.Log("Connected to server.");
        }
        catch (Exception e)
        {
            Debug.LogError("Error connecting to server: " + e.Message);
            TryReconnect();
        }
    }

    void ReceiveData()
    {
        byte[] buffer = new byte[1024];
        while (isConnected)
        {
            try
            {
                int bytesRead = stream.Read(buffer, 0, buffer.Length);
                if (bytesRead > 0)
                {
                    string receivedMessage = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                    if (receivedMessage != null)
                    {
                        Debug.Log("Received from server: " + receivedMessage);

                        // 如果是心跳响应,通知主线程更新心跳时间
                        if (receivedMessage.Equals("heartbeat"))
                        {
                            updateHeartbeat = true;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Debug.LogError("Error receiving data: " + e.Message);
                Disconnect();
                break;
            }
        }
    }

    public void SendData(string message)
    {
        if (isConnected)
        {
            try
            {
                byte[] data = Encoding.UTF8.GetBytes(message);
                stream.Write(data, 0, data.Length);
                Debug.Log("Sent to server: " + message);
            }
            catch (Exception e)
            {
                Debug.LogError("Error sending data: " + e.Message);
                Disconnect();
            }
        }
    }

    void Disconnect()
    {
        if (isConnected)
        {
            isConnected = false;
            if (receiveThread != null && receiveThread.IsAlive)
                receiveThread.Abort();
            if (stream != null)
                stream.Close();
            if (client != null)
                client.Close();
            Debug.Log("Disconnected from server.");

            // 尝试重连
            TryReconnect();
        }
    }

    void TryReconnect()
    {
        Debug.Log("触发重连机制");
        if (reconnectAttempts < maxReconnectAttempts)
        {
            reconnectAttempts++;
            Debug.Log($"Attempting to reconnect ({reconnectAttempts}/{maxReconnectAttempts})...");
            Invoke("ConnectToServer", reconnectInterval); // 2秒后重连
        }
        else
        {
            Debug.Log("Max reconnection attempts reached. Giving up.");
        }
    }

    void OnDestroy()
    {
        Disconnect();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2337441.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络安全领域的AI战略准备:从概念到实践

网络安全领域的AI准备不仅涉及最新工具和技术的应用&#xff0c;更是一项战略必需。许多企业若因目标不明确、数据准备不足或与业务重点脱节而未能有效利用AI技术&#xff0c;可能面临严重后果&#xff0c;包括高级网络威胁数量的激增。 AI准备的核心要素 构建稳健的网络安全…

MacOs下解决远程终端内容复制并到本地粘贴板

常常需要在服务器上捣鼓东西&#xff0c;同时需要将内容复制到本地的需求。 1-内容是在远程终端用vim打开&#xff0c;如何用vim的类似指令达到快速复制到本地呢&#xff1f; 假设待复制的内容&#xff1a; #include <iostream> #include <cstring> using names…

基于PAI+专属网关+私网连接:构建全链路 Deepseek 云上私有化部署与模型调用架构

DeepSeek - R1 是由深度求索公司推出的首款推理模型&#xff0c;该模型在数学、代码和推理任务上的表现优异&#xff0c;市场反馈火爆。在大模型技术商业化进程中&#xff0c;企业级用户普遍面临四大核心挑战&#xff1a; 算力投入成本高昂&#xff1a;构建千亿参数级模型的训…

【cocos creator 3.x】cocos creator2.x项目升级3.x项目改动点

1、基本改动 基本改动&#xff1a;去掉了cc.&#xff0c;改成在顶部添加导入 项目升级时候直接将cc.去掉&#xff0c;根据提示添加引用 node只保留position,scale,rotation,layer 其余属性如opacity&#xff0c;如果需要使用需要在节点手动添加UIOpacity组件 3d层和ui层分开…

List基础与难度题

1. 向 ArrayList 中添加元素并打印 功能描述&#xff1a; 程序创建一个空的 ArrayList 集合&#xff0c;用于存储字符串类型的元素。向该 ArrayList 中依次添加指定的字符串元素。使用增强型 for 循环遍历 ArrayList 中的所有元素&#xff0c;并将每个元素打印输出到控制台。 …

Oracle19C低版本一天遭遇两BUG(ORA-04031/ORA-600)

昨天帮朋友看一个系统异常卡顿的案例&#xff0c;在这里分享给大家 环境&#xff1a;Exadata X8M 数据库版本19.11 1.系统报错信息 表象为系统卡顿&#xff0c;页面无法刷出&#xff0c;登陆到主机上看到节点1 系统等待存在大量的 cursor: pin S wait on X等待 查看两个节…

【4.1.-4.20学习周报】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 摘要Abstract一、方法介绍1.1HippoRAG 1.2HippoRAG2二、实验2.1实验概况2.2实验代码2.3实验结果 总结 摘要 本博客介绍了论文《From RAG to Memory: Non-Parametri…

python学习—合并多个word文档

系列文章目录 python学习—合并TXT文本文件 python学习—统计嵌套文件夹内的文件数量并建立索引表格 python学习—查找指定目录下的指定类型文件 python学习—年会不能停&#xff0c;游戏抽签抽奖 python学习—循环语句-控制流 python学习—合并多个Excel工作簿表格文件 pytho…

[Python] UV工具入门使用指南——小试牛刀

背景 MCP开发使用到了uv&#xff0c;简单记录一下&#xff1a; 为什么MCP更推荐使用uv进行环境管理&#xff1f; MCP 依赖的 Python 环境可能包含多个模块&#xff0c;uv 通过 pyproject.toml 提供更高效的管理方式&#xff0c;并且可以避免 pip 的一些依赖冲突问题。…

PclSharp ——pcl的c#nuget包

简介&#xff1a; NuGet Gallery | PclSharp 1.8.1.20180820-beta07 下载.NET Framework 4.5.2 Developer Pack&#xff1a; 下载 .NET Framework 4.5.2 Developer Pack Offline Installer 离线安装nupkg&#xff1a; nupkg是visual studio 的NuGet Package的一个包文件 安…

MGR实现mysql高可用性

一。MGR和PXC的区别 1. PXC的消息广播机制是在节点间循环的&#xff0c;需要所有节点都确认消息&#xff0c;因此只要有一个节点故障&#xff0c;则会导致整个PXC都发生故障。而MGR则是多数派投票模式&#xff0c;个别少数派节点故障时&#xff0c;一般不影响整体的可用性。这…

新型多机器人协作运输系统,轻松应对复杂路面

受到鱼类、鸟类和蚂蚁等微小生物体协作操纵的启发&#xff0c;研究人员开发了多机器人协作运输系统&#xff08;Multirobot Cooperative Transportation Systems&#xff0c;MRCTS&#xff09;运输单个机器人无法处理的重型超大物体&#xff0c;可用于搜救行动、灾难响应、军事…

【秣厉科技】LabVIEW工具包——OpenCV 教程(19):拾遗 - imgproc 基础操作(上)

文章目录 前言imgproc 基础操作&#xff08;上&#xff09;1. 颜色空间2. 直方图3. 二值化4. 腐蚀、膨胀、开闭运算5. 梯度与轮廓6. 简易绘图7. 重映射 总结 前言 需要下载安装OpenCV工具包的朋友&#xff0c;请前往 此处 &#xff1b;系统要求&#xff1a;Windows系统&#x…

学习笔记:金融经济学 第3讲

学习笔记&#xff1a;金融经济学 第3讲 注&#xff1a;A本金&#xff0c;n时间&#xff08;比如年&#xff09;&#xff0c;r利率一、 计算习惯1. 单息&#xff08;新产生的利息不算进本金重新计算利息&#xff0c;收款额A(1nr) &#xff09;2. 复利(新产生的利息算进本金重新计…

NVIDIA RTX™ GPU 低成本启动零售 AI 场景开发

零售行业正在探索应用 AI 升级客户体验&#xff0c;同时优化内部流程。面对多重应用场景以及成本优化压力&#xff0c;团队可采用成本相对可控的方案&#xff0c;来应对多重场景的前期项目预演和落地&#xff0c;避免短期内大规模投入造成的资源浪费。 客户体验 AI 场景的研究…

【网络】IP层的重要知识

目录 1.IP层的作用 2.主机和节点 3.网络层和数据链路层的关系 4.路由控制 4.1.路由控制的过程 4.2. IP地址与路由控制 4.3.路由控制表的聚合 4.4.静态路由和动态路由 4.5.动态路由的基础 5.数据链路的抽象化 5.1.数据链路不同&#xff0c;MTU则相异 5.2.路径MTU发…

OpenCV 模板匹配方法详解

文章目录 1. 什么是模板匹配&#xff1f;2. 模板匹配的原理2.1数学表达 3. OpenCV 实现模板匹配3.1基本步骤 4. 模板匹配的局限性5. 总结 1. 什么是模板匹配&#xff1f; 模板匹配&#xff08;Template Matching&#xff09;是计算机视觉中的一种基础技术&#xff0c;用于在目…

一键解锁Landsat 9地表温度计算!ENVI与ArcGIS Pro全流程详解(无需NASA大气校正)

为什么选择Landsat 9的L2SP数据&#xff1f; 之前&#xff1a;《ArcGIS与ENVI——基于landsat与Modis影像的遥感技术的生态环境质量评价》&#xff0c;基于Landsat前期的产品计算温度反演数据需要一系列复杂的步骤。 现在&#xff1a; Landsat 8-9的Collection 2 Level-2&…

RK3588的linux下实现HDMI输出分辨率及帧率的裁剪

bug反馈&#xff1a;客户现场反馈hdmi接显示屏出现概率性闪黑屏&#xff0c;排除线材&#xff0c;显示屏及GND等外部因素后&#xff0c;提出尝试降低hdmi的输出分辨率和帧率对比测试看看。 Step1&#xff1a;先直接在linux的sdk中找到板卡编译生成后的dts找到hdmi节点 然后找到…

XR技术赋能艺术展演|我的宇宙推动东方美学体验化

本次广州展览现场引入我的宇宙XR体验模块&#xff0c;通过空间计算与动作捕捉技术&#xff0c;让观众在潮玩艺术氛围中体验虚拟互动&#xff0c;打造“看得懂也玩得动”的展演新场景。 作为科技与文化融合的推动者&#xff0c;我的宇宙正在以“体验科技”为媒介&#xff0c;为潮…