C#TcpClient,可以设置死锁(2个单独的程序通过TcpClient进行通信)

时间:2013-12-30 02:13:47

标签: c# deadlock tcpclient

我有两个独立的程序实例在不同的计算机上运行。它们通过TcpClient相互连接(井2连接,进入和传出,我知道TcpClient允许消息两种方式,但为了简化代码,更容易建立2个连接。)

两者都发送了大量消息,然后将在缓冲区中发送更多数据。两个线程都经历了一个阅读循环然后写作。

ReceiveIncomingMessagesFromTcpClient()
ParseAndDeliverIncomingMessagesInternaly()
CollectNewOutgoingMessagesInternaly()
DeliverOutgoingMessagesViaTcpClient()

我的程序似乎运行良好,然后停止发送消息,所以我担心这样做的原因是两者都试图将Outgoing消息写入各自的流,两个流都已满,并且没有任何内容可以清空我有一个陷入僵局的程序。

如果是这种情况,请在开始发送消息之前验证缓冲区中是否有空间,并且仅发送空间是什么? (为了避免阻塞发送,并由此导致死锁?)


我有这个发送数据的函数,它可以连续调用很多次。连接的缓冲区大小是默认的8kb。

因此,如果此操作连续多次重复,则8kb缓冲区将被填充,而stream.Write()是一个阻塞语句,直到另一方提取数据。

由于另一方是完美的镜像,如果它已经拉出大量数据要发送,双方可能会同时尝试发送,导致死锁。

private void DeliverOutgoingMessagesViaTcpClient()
{
    for(int i = 0; i < message.Count;i++)
    {
        //send request
        Byte[] data = System.Text.Encoding.ASCII.GetBytes(message[i]);
        NetworkStream stream = connection.GetStream();
        stream.Write(data, 0, data.Length);
    }
}

1 个答案:

答案 0 :(得分:0)

根据反馈,似乎我的问题是当两个通信程序都阻止其发送消息时,我冒着阻止的风险。所以我现在将发送功能移到了一个单独的类中。

以下是我决定使用的课程。它应该在准备好使用后启动,并且可以使用目标TcpClient和消息调用,可以通过阻塞或非阻塞来执行调用(非阻塞返回true,接受消息)

    public class TcpSender
    {
        Mutex moveToInternal = new Mutex();
        Mutex startOnce = new Mutex();

        List<Message> internalMessages = new List<Message>();
        List<Message> externalMessages = new List<Message>();

        Thread mainLoop;

        String status = "stopped";

        //returns true if started.
        public Boolean Start()
        {
            if (startOnce.WaitOne(0))
            {
                status = "started";
                mainLoop = new Thread(new ThreadStart(InternalMainLoop));
                mainLoop.IsBackground = true;
                mainLoop.Start();
                return true;
            }
            return false;
        }

        private void InternalMainLoop()
        {

                while (status == "started")
                {
                    Boolean didSomething = false;

                    //pickup new messages and move to internal buffer
                    if (externalMessages.Count > 0)
                    {
                        didSomething = true;
                        if (moveToInternal.WaitOne())
                        {
                            internalMessages.AddRange(externalMessages);
                            externalMessages.Clear();
                            moveToInternal.ReleaseMutex();
                        }
                    }

                    //deliver messages
                    for (int i = 0; i < internalMessages.Count; i++)
                    {
                        //send request
                        Byte[] data = System.Text.Encoding.ASCII.GetBytes(internalMessages[i].GetMessage());
                        NetworkStream stream = internalMessages[i].GetClient().GetStream();
                        stream.Write(data, 0, data.Length);
                    }
                    internalMessages.Clear();

                    if (!didSomething) Thread.Sleep(1); //Nothing to receive or send, maybe tomorr... next milisec there is something to do
                }
                startOnce.ReleaseMutex();
        }

        public void Stop()
        {
            status = "stopped";
        }

        public Boolean Status()
        {
            return (status == "started");
        }

        //return true if message was accepted
        public Boolean addMessageNonBlocking(TcpClient client, String message)
        {
            Boolean gotMutex = false;
            if(gotMutex = moveToInternal.WaitOne(0));
            {
                Message tmpMessage = new Message(client, message);
                externalMessages.Add(tmpMessage);
                moveToInternal.ReleaseMutex();
            }
            return gotMutex;
        }

        public void addMessageBlocking(TcpClient client, String message)
        {
            //loop until message is delivered
            while (!addMessageNonBlocking(client, message))
            {
                Thread.Sleep(1); //could skip this, but no need for busy waiting
            }
        }

        private class Message
        {
            TcpClient client;
            String message;

            public Message(TcpClient client, String message)
            {
                this.client = client;
                this.message = message;
            }

            public TcpClient GetClient()
            {
                return client;
            }

            public String GetMessage()
            {
                return message;
            }
        }
    }

用于验证TcpClient是否阻止的测试代码

将此代码添加到可以运行的任何位置。 testClient必须是连接到任何地方的TcpClient。

int i = 0;
String testmsg = "This is a 25 char test!!!";
Byte[] data = System.Text.Encoding.ASCII.GetBytes(testmsg);
while (true)
{
    i++;
    testClient.GetStream().Write(data, 0, data.Length);
    if (i % 10 == 0) Console.WriteLine("Cycle Count: " + i);
}