【问题标题】:Wait for incoming tcp/ip server request等待传入的 tcp/ip 服务器请求
【发布时间】:2018-11-02 11:13:52
【问题描述】:

我正在为我的应用程序与仓库系统建立 TCP/IP 连接。沟通是这样的。

  • 我向仓库系统的 TCP/IP(Socket) 服务器发送消息。
  • 仓库系统以一条消息响应我的本地 TCP/IP 服务器。

因此没有直接响应消息。相反,每个应用程序都作为它自己的服务器。 但我希望我的应用程序等待来自其他服务器的响应。

所以基本上我有以下代码。

public string ControllerFunction() {
    startLocalTcpIpServer();
    sendMessage("a message");

    return clientMessage;
}

这是我自己的服务器,使用 start() 函数启动

    public void Start() {
        // Start TcpServer background thread        
        tcpListenerThread = new Thread(new ThreadStart(ListenForIncommingRequests)) {
            IsBackground = true
        };
        tcpListenerThread.Start();
    }

    private void ListenForIncommingRequests() {
        try {
            tcpListener = new TcpListener(IPAddress.Parse(serverIp), port);
            tcpListener.Start();
            byte[] bytes = new byte[1024];
            Console.WriteLine("Server Started");
            while(true) {
                // Get a stream object for reading 
                using(NetworkStream stream = tcpListener.AcceptTcpClient().GetStream()) {
                    int length;
                    // Read incomming stream into byte arrary.                      
                    while((length = stream.Read(bytes, 0, bytes.Length)) != 0) {
                        byte[] incommingData = new byte[length];
                        Array.Copy(bytes, 0, incommingData, 0, length);
                        // Convert byte array to string message.                            
                        string clientMessage = Encoding.ASCII.GetString(incommingData);
                    }
                }
            }
        }
        catch(SocketException socketException) {
            Console.WriteLine("SocketException " + socketException.ToString());
        }
    }

所以我想再次使用结果字符串clientMessage 作为我的 ControllerFunction 的返回值。但是如何以正确的方式获取数据呢?

【问题讨论】:

  • 你必须发回消息。可能用某种 id 包装它以识别不同的消息。
  • 我无法控制返回消息。因此我不能在那里添加一个ID。否则我会直接制作一个服务器系统。但我可以很确定我得到的响应是对最后发送消息的响应。
  • 那么有3台服务器? A发送给B,B发送回复给C?
  • 无论如何,只要设置一个 TcpListener 并等待消息并返回它。
  • 无程序 A 向程序 B 的服务器发送消息(作为客户端)。程序 B 向程序 A 的服务器(作为客户端)发送消息。

标签: c# .net asynchronous tcp-ip


【解决方案1】:

因此,您需要能够等待来自应用程序中另一个位置(本地服务器)的响应。响应将首先在那里触发。本地服务器应该有一个您可以订阅的事件(在我的示例中为OnMessage)。此事件会将结果消息转发给您。

可以使用 TaskCompletionSource 处理同步。您将创建可用于同步或异步获取结果的任务。

类似这样的:

public string ControllerFunction()
{           
    return ControllerFunctionTask().Result;
}

public Task<string> ControllerFunctionTask()
{            
    sendMessage("a message");

    var task = new TaskCompletionSource<string>();
    localServer.OnMessage = (message) =>
    {
        task.SetResult(message);
    };

    return task.Task;
}

如cmets中所说,同步等待异步Task可能会导致死锁。当调用者线程是context 线程(UI,ASP)时,可能会发生这种情况。因此这应该是更好的方法:

public async Task<string> ControllerFunction()
{           
    return await ControllerFunctionTask();
}

public Task<string> ControllerFunctionTask()
{            
    sendMessage("a message");

    var task = new TaskCompletionSource<string>();
    localServer.OnMessage = (message) =>
    {
        task.SetResult(message);
    };

    return task.Task;
}


OnMessage 可以这样定义:

public event Action<string> OnMessage;

然后它会在你得到 clientMessage 字符串的那一行之后被调用:

string clientMessage = Encoding.ASCII.GetString(incommingData);
if (OnMessage != null)
     OnMessage(clientMessage);

【讨论】:

  • return ControllerFunctionTask().Result; 是个坏主意,您阻塞了异步调用。如果您使用异步,您需要使用线程池线程或制作所有内容async
  • 从问题中不清楚调用者是上下文线程还是线程池线程。我知道如果在上下文线程(UI,ASP,...)中使用它会导致死锁。我更新了我的答案
  • 看起来这很有帮助。你能告诉我localServer.OnMessage 会是什么样子吗?
  • 公共事件动作 OnMessage;我会更新答案
猜你喜欢
  • 2011-07-27
  • 2014-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-16
  • 2017-02-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多