You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

【C#】通过中间服务器实现客户端向指定客户端传输数据

问题需求

我找了很多资料,都是向所有客户端发消息的方案,但我要实现的是:多个客户端向服务器发起请求,获取另一客户端的数据;目标客户端把数据发给服务器,同时指明该数据要发给发起请求的客户端。我是新手,不知道怎么实现。

目标流程

客户端A(请求方)→ 服务器 → 客户端B(数据提供方),随后客户端B→服务器→客户端A,实现定向的数据传输,规避公网环境下的端口转发问题。

现有尝试

我试过让数据发送端监听,请求端直接连接发送端传输数据,局域网里能用,但公网环境需要端口转发。用户群体庞大,没法给每个用户配置端口转发,所以打算用一台服务器做中转节点。

我写了个控制台测试服务器,监听IP:端口X接收客户端连接和数据,再转发到IP:端口Y,但这样会把数据发给端口Y上的所有客户端。我没法直接向客户端公网IP发送数据,也不想给每个交互的客户端分配新端口,希望找到不用分配新端口的方式实现请求与数据接收。

现有测试代码

服务器代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Test___server
{
    class server
    {
        public static string serverIP = "192.168.0.102";
        static void Main(string[] args)
        {
            Thread listenSendingThread = new Thread(listenSending);
            listenSendingThread.IsBackground = true;
            listenSendingThread.Start();
            Thread listenReceivingThread = new Thread(listenReceiving);
            listenReceivingThread.IsBackground = true;
            listenReceivingThread.Start();
            Console.ReadKey();
        }

        public static List<TcpClient> listSending = new List<TcpClient>();
        public static List<TcpClient> listReceiving = new List<TcpClient>();
        public static TcpClient clientSending = null;
        private static void listenSending()
        {
            TcpListener listenerSending = new TcpListener(IPAddress.Parse(serverIP), 5319);
            listenerSending.Start();
            Console.WriteLine("Server listening to " + serverIP + ":5319");
            while(true)
            {
                clientSending = listenerSending.AcceptTcpClient();
                listSending.Add(clientSending);
                Console.WriteLine("Sender connection received from " + clientSending.Client.RemoteEndPoint);
            }
        }
        private static void send()
        {
            StreamWriter sw = new StreamWriter(clientSending.GetStream());
            sw.WriteLine(message);
            sw.Flush();
            Console.WriteLine("Message sent!");
        }

        public static string message = string.Empty;
            
        private static void listenReceiving()
        {
            TcpListener listener = new TcpListener(IPAddress.Parse(serverIP), 0045);
            listener.Start();
            Console.WriteLine("Server listening to " + serverIP + ":0045");
            while (true)
            {
                TcpClient client = listener.AcceptTcpClient();
                listReceiving.Add(client);
                Console.WriteLine("Receiver connection received from " + client.Client.RemoteEndPoint);
                StreamReader sr = new StreamReader(client.GetStream());
                message = sr.ReadLine();
                send();
            }
        }
    }
}

请求客户端代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Test____admin
{
    class admin
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Begin");
            string serverIP = "192.168.0.102";
            System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
            clientSocket.Connect(serverIP,  );
            Console.WriteLine("Connected");
            while (true)
            {
                Console.WriteLine("Reading");
                StreamReader sr = new StreamReader(clientSocket.GetStream());
                Console.WriteLine("Message: " + sr.ReadLine());
            }
        }
    }
}

响应请求的客户端代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Test___client
{
    class client
    {
        public static string serverIP = "192.168.0.102";
        static void Main(string[] args)
        {
            clientConnect();
        }

        private static void clientConnect()
        {
            try
            {
                TcpClient client = new TcpClient(serverIP, 0045);
                StreamWriter sw = new StreamWriter(client.GetStream());
                sw.WriteLine("Karan!");
                sw.Flush();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}

解决方案

核心思路是给每个客户端分配唯一标识,服务器通过标识管理连接并定向转发数据,无需新增端口,只用一个固定端口即可。

关键改进点

  1. 客户端标识绑定:每个客户端连接服务器时发送唯一ID(如用户名、UUID),服务器用Dictionary<string, TcpClient>存储ID与连接的映射,替代原有的List<TcpClient>。
  2. 统一消息协议:定义包含目标ID、内容的消息格式,让服务器能识别数据的接收方。
  3. 异步连接处理:每个客户端连接后开启独立异步线程处理消息读写,避免单线程阻塞。

改进后的示例代码

服务器代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace TestServer
{
    class Server
    {
        // 存储客户端ID与连接的映射
        private static Dictionary<string, TcpClient> _clientMap = new Dictionary<string, TcpClient>();
        private static TcpListener _listener;
        private const int Port = 5319; // 只用一个固定端口
        private const string ServerIp = "192.168.0.102";

        static void Main(string[] args)
        {
            _listener = new TcpListener(IPAddress.Parse(ServerIp), Port);
            _listener.Start();
            Console.WriteLine($"服务器启动,监听 {ServerIp}:{Port}");
            AcceptClientsAsync();
            Console.ReadKey();
        }

        // 异步接收客户端连接
        private static async void AcceptClientsAsync()
        {
            while (true)
            {
                var client = await _listener.AcceptTcpClientAsync();
                Console.WriteLine($"新客户端连接:{client.Client.RemoteEndPoint}");
                // 异步处理该客户端的消息
                _ = HandleClientAsync(client);
            }
        }

        // 处理单个客户端的消息读写
        private static async Task HandleClientAsync(TcpClient client)
        {
            string clientId = null;
            var stream = client.GetStream();
            var reader = new StreamReader(stream, Encoding.UTF8);
            var writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true };

            try
            {
                // 第一步:接收客户端的唯一ID
                clientId = await reader.ReadLineAsync();
                if (!string.IsNullOrEmpty(clientId))
                {
                    lock (_clientMap)
                    {
                        _clientMap[clientId] = client;
                        Console.WriteLine($"客户端 {clientId} 注册成功");
                    }
                }

                // 持续接收消息
                string message;
                while ((message = await reader.ReadLineAsync()) != null)
                {
                    Console.WriteLine($"收到 {clientId} 的消息:{message}");
                    // 解析消息:格式为 "目标ID|消息内容"
                    var parts = message.Split('|');
                    if (parts.Length == 2)
                    {
                        var targetId = parts[0];
                        var content = parts[1];
                        // 定向转发给目标客户端
                        if (_clientMap.TryGetValue(targetId, out var targetClient))
                        {
                            var targetWriter = new StreamWriter(targetClient.GetStream(), Encoding.UTF8) { AutoFlush = true };
                            await targetWriter.WriteLineAsync($"来自 {clientId} 的数据:{content}");
                            Console.WriteLine($"已转发消息到 {targetId}");
                        }
                        else
                        {
                            await writer.WriteLineAsync($"错误:目标客户端 {targetId} 未在线");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"客户端 {clientId} 连接异常:{ex.Message}");
            }
            finally
            {
                // 客户端断开后移除映射
                if (!string.IsNullOrEmpty(clientId))
                {
                    lock (_clientMap)
                    {
                        _clientMap.Remove(clientId);
                        Console.WriteLine($"客户端 {clientId} 已断开连接");
                    }
                }
                client.Close();
            }
        }
    }
}

请求客户端代码(客户端A)

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace RequestClient
{
    class RequestClient
    {
        private const string ServerIp = "192.168.0.102";
        private const int Port = 5319;
        private const string ClientId = "ClientA"; // 自己的唯一标识

        static async Task Main(string[] args)
        {
            using var client = new TcpClient();
            await client.ConnectAsync(ServerIp, Port);
            Console.WriteLine("已连接到服务器");

            var stream = client.GetStream();
            var writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true };
            var reader = new StreamReader(stream, Encoding.UTF8);

            // 发送自己的ID到服务器
            await writer.WriteLineAsync(ClientId);

            // 发送请求给目标客户端(比如ClientB)
            Console.WriteLine("输入要发送给ClientB的请求内容:");
            var requestContent = Console.ReadLine();
            await writer.WriteLineAsync($"ClientB|{requestContent}");

            // 等待接收响应
            Console.WriteLine("等待接收响应...");
            var response = await reader.ReadLineAsync();
            Console.WriteLine($"收到响应:{response}");

            Console.ReadKey();
        }
    }
}

响应客户端代码(客户端B)

using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ResponseClient
{
    class ResponseClient
    {
        private const string ServerIp = "192.168.0.102";
        private const int Port = 5319;
        private const string ClientId = "ClientB"; // 自己的唯一标识

        static async Task Main(string[] args)
        {
            using var client = new TcpClient();
            await client.ConnectAsync(ServerIp, Port);
            Console.WriteLine("已连接到服务器");

            var stream = client.GetStream();
            var writer = new StreamWriter(stream, Encoding.UTF8) { AutoFlush = true };
            var reader = new StreamReader(stream, Encoding.UTF8);

            // 发送自己的ID到服务器
            await writer.WriteLineAsync(ClientId);

            Console.WriteLine("等待接收请求...");
            // 持续监听请求
            string message;
            while ((message = await reader.ReadLineAsync()) != null)
            {
                Console.WriteLine($"收到请求:{message}");
                // 解析请求来源(格式:"来自 ClientA 的数据:XXX")
                var parts = message.Split(':');
                if (parts.Length >= 2)
                {
                    var sourceId = parts[0].Replace("来自 ", "");
                    // 准备响应内容
                    Console.WriteLine("输入要回复的内容:");
                    var responseContent = Console.ReadLine();
                    // 发送响应给请求来源
                    await writer.WriteLineAsync($"{sourceId}|{responseContent}");
                    Console.WriteLine("响应已发送");
                }
            }
        }
    }
}

说明

  1. 所有客户端都连接同一个服务器端口,无需额外端口配置。
  2. 服务器通过客户端ID精准管理连接,实现定向数据转发。
  3. 消息用|分隔目标ID和内容,新手易理解修改;也可换成JSON格式(如用Newtonsoft.Json序列化),提升扩展性。
  4. 异步处理机制支持多客户端同时连接,避免单线程阻塞问题。

内容的提问来源于stack exchange,提问作者karan ugale

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 17:10:36