【问题标题】:Making a Thread Safe socket class in C# [closed]在 C# 中创建线程安全套接字类 [关闭]
【发布时间】:2014-06-02 19:17:11
【问题描述】:

我对@9​​87654321@ 和Thread Safe 套接字的一些概念有一些疑问。我有 2 个类,一个 服务器类 和一个 客户端类

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

// Loosely inspired on http://msdn.microsoft.com/en-us/library/fx6588te.aspx

namespace AsynchronousSockets
{
    class Server
    {
        class StateObject
        {
            public Socket connection = null;

            // Note that I use a very small buffer size
            // for this example. Normally you'd like a much
            // larger buffer. But this small buffer size nicely
            // demonstrates getting the entire message in multiple
            // pieces.
            public const int bufferSize = 100000; 
            public byte[] buffer = new byte[bufferSize];
            public int expectedMessageLength = 0;
            public int receivedMessageLength = 0;
            public byte[] message = null;
        }

        static ManualResetEvent acceptDone = new ManualResetEvent(false);
        const int listenPort = 2500;

        static void Main(string[] args)
        {
            Console.Out.WriteLine("This is the server");

            IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, listenPort);
            Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                listener.Bind(localEndPoint);
                listener.Listen(100);

                while (true)
                {
                    acceptDone.Reset();

                    Console.Out.WriteLine("Listening on port {0}", listenPort);
                    listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);

                    acceptDone.WaitOne();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }        

        static void AcceptCallback(IAsyncResult ar)
        {
            try
            {
                acceptDone.Set();

                Socket listener = (Socket)ar.AsyncState;
                Socket handler = listener.EndAccept(ar);

                StateObject state = new StateObject();
                state.connection = handler;

                handler.BeginReceive(state.buffer, 0, StateObject.bufferSize,
                    SocketFlags.None, new AsyncCallback(ReadCallback), state);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        static void ReadCallback(IAsyncResult ar)
        {
            try
            {
                StateObject state = (StateObject)ar.AsyncState;
                Socket handler = state.connection;

                int read = handler.EndReceive(ar);

                if (read > 0)
                {
                    Console.Out.WriteLine("Read {0} bytes", read);

                    if (state.expectedMessageLength == 0)
                    {
                        // Extract how much data to expect from the first 4 bytes
                        // then configure buffer sizes and copy the already received
                        // part of the message.
                        state.expectedMessageLength = BitConverter.ToInt32(state.buffer, 0);
                        state.message = new byte[state.expectedMessageLength];
                        Array.ConstrainedCopy(state.buffer, 4, state.message, 0, Math.Min(StateObject.bufferSize - 4, state.expectedMessageLength - state.receivedMessageLength));
                        state.receivedMessageLength += read - 4;
                    }
                    else
                    {
                        Array.ConstrainedCopy(state.buffer, 0, state.message, state.receivedMessageLength, Math.Min(StateObject.bufferSize, state.expectedMessageLength - state.receivedMessageLength));
                        state.receivedMessageLength += read;
                    }                                       

                    // Check if we received the entire message. If not
                    // continue listening, else close the connection
                    // and reconstruct the message.
                    if (state.receivedMessageLength < state.expectedMessageLength)
                    {
                        handler.BeginReceive(state.buffer, 0, StateObject.bufferSize,
                            SocketFlags.None, new AsyncCallback(ReadCallback), state);
                    }
                    else
                    {
                        handler.Shutdown(SocketShutdown.Both);
                        handler.Close();

                        Console.Out.WriteLine("Received message: \n");
                        Console.Out.WriteLine(Encoding.UTF8.GetString(state.message));
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}



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

// Loosely inspired on http://msdn.microsoft.com/en-us/library/bew39x2a.aspx

namespace AsynchronousSockets
{
    class Program
    {
        static readonly IPAddress serverIP = IPAddress.Loopback;
        const int serverPort = 2500;

        static ManualResetEvent connectDone = new ManualResetEvent(false);
        static ManualResetEvent sendDone = new ManualResetEvent(false);


        static void Main(string[] args)
        {
            Console.Out.WriteLine("This is the client");
            Console.Out.WriteLine("Write the message to send. End with an emtpy line to start the transmisison. \n");

            string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;

            for (int i = 0; i <= 10000; i++)
            {
                String message = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss") + " : Message sended by " + userName + ".";

                Console.Out.WriteLine("Sending message: ...\n");
                Console.Out.Write(message);
                Console.Out.Write("\n");

                Thread.Sleep(10);
                Console.Out.WriteLine("Sleeping ...\n");

                SendMessageAsync(message);
            }

            Console.Out.WriteLine("Sending finished by " + userName + "! \n");
        }

        static void SendMessageAsync(string message)
        {                        
            // Initiate connecting to the server
            Socket connection = Connect();

            // block this thread until we have connected
            // normally your program would just continue doing other work
            // but we've got nothing to do :)
            connectDone.WaitOne();
            Console.Out.WriteLine("Connected to server");

            // Start sending the data
            SendData(connection, message);
            sendDone.WaitOne();
            Console.Out.WriteLine("Message successfully sent");
        }        

        static Socket Connect()
        {
            try
            {             
                IPEndPoint serverAddress = new IPEndPoint(serverIP, serverPort);
                Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                client.BeginConnect(serverAddress, new AsyncCallback(ConnectCallback), client);

                return client;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);

                return null;               
            }
        }

        static void SendData(Socket connection, string message)
        {
            try
            {
                byte[] data = Encoding.UTF8.GetBytes(message);

                // We store how much data the server should expect
                // in the first 4 bytes of the data we're going to send
                byte[] head = BitConverter.GetBytes(data.Length);

                byte[] total = new byte[data.Length + head.Length];
                head.CopyTo(total, 0);
                data.CopyTo(total, head.Length);

                connection.BeginSend(total, 0, total.Length, 0, new AsyncCallback(SendCallBack), connection);
            }
            catch (Exception e)
            {
                Console.Out.WriteLine(e.Message);
            }
        }

        private static void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                Socket client = (Socket)ar.AsyncState;
                client.EndConnect(ar);
                connectDone.Set();
            }
            catch (Exception e)
            {
                Console.Out.WriteLine(e.Message);
            }
        }

        private static void SendCallBack(IAsyncResult ar)
        {
            try
            {
                Socket client = (Socket)ar.AsyncState;
                int bytes = client.EndSend(ar);

                Console.Out.WriteLine("A total of {0} bytes were sent to the server", bytes);

                sendDone.Set();
            }
            catch (Exception e)
            {
                Console.Out.WriteLine(e.Message);
            }
        }
    }
}

如您所见,一旦client.exe 启动,如果server.exe 正在运行,它将收到Client 类发送的一些消息。

for (int i = 0; i <= 10000; i++)
{
    String message = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss") + " : Message sended by " + userName + ".";

    Console.Out.WriteLine("Sending message: ...\n");
    Console.Out.Write(message);
    Console.Out.Write("\n");

    Thread.Sleep(10);
    Console.Out.WriteLine("Sleeping ...\n");

    SendMessageAsync(message);
}

这个循环将执行 10000 次,循环之间的暂停时间为 10 毫秒。我从 3 个地方启动了 3 个客户端(不同的 windows 用户同时登录),服务器日志是:

......
02-06-2014 11:24:30 : Message sended by MyComputer-PC\user1.
Listening on port 2500
Listening on port 2500
Listening on port 2500
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 7 bytes
Received message: 

02-06-2014 11:24:30 : Message sended by MyComputer-PC\user2.
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 7 bytes
Received message: 

02-06-2014 11:24:30 : Message sended by MyComputer-PC\user3.
Listening on port 2500
Listening on port 2500
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 7 bytes
Received message: 

02-06-2014 11:24:30 : Message sended by MyComputer-PC\user2.
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 7 bytes
Received message: 

02-06-2014 11:24:30 : Message sended by MyComputer-PC\user3.
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 7 bytes
Received message: 

02-06-2014 11:24:30 : Message sended by MyComputer-PC\user1.
Listening on port 2500
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 8 bytes
Read 7 bytes
Received message:
......

在所有 3 个客户端停止后,我在“记事本++”中打开日志文件并计算以下结果:

count "MyComputer-PC\user1" => 8903

count "MyComputer-PC\user2" => 8464

count "MyComputer-PC\user3" => 8990

为什么会这样?一些数据已经丢失,它应该呈现 10.000 10.000 10.000 ...

我该如何解决这个问题?

我想问的另一件事是如何使套接字线程安全。

编辑

当套接字被拒绝时,我实际上得到了这个日志

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent
Sending message: ...

03-06-2014 09:35:58 : Message sended by MyComputer-PC\User1.
Sleeping ...

connection.Connected False
connection.Blocking True
Connected to server
A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
Message successfully sent

谢谢。

【问题讨论】:

  • 你为什么搁置我的帖子?我想我没有违反 stackoverflow 的原则
  • 由于列出的原因而暂停。范围太广了。
  • 它是什么?请解释一下,我可能会一遍又一遍地犯同样的错误。谢谢。
  • 关闭原因在此处列出。你只需要阅读它。
  • There are either too many possible answers, or good answers would be too long for this format. Please add details to narrow the answer set or to isolate an issue that can be answered in a few paragraphs. 所以我写了很多?这是不可能的......我想做的只是提供足够的细节,而不是像其他人所说的那样说这不起作用......

标签: c# asp.net .net multithreading sockets


【解决方案1】:

问题似乎是在客户端中使用了ManualResetEvents。请记住,ManualResetEvent 必须手动重置,否则在事件被 Set() 之后的所有 WaitOne() 调用将立即返回。因此,在发送第一条消息后,您的客户端在尝试发送数据之前不会等待连接套接字,正如我在机器上运行它时看到的以下消息所示:

由于未连接套接字并且(在使用 sendto 调用在数据报套接字上发送时)未提供地址,因此不允许发送或接收数据的请求

尝试在客户端中将您的 ManualResetEvents 更改为 AutoResetEvents(在 WaitOne() 返回 true 后自动重置),这应该可以解决问题。

【讨论】:

  • 1-> ManualResetEvent must be manually reset -> 如何手动重置?这是一个命令? 2->所以基本上你说的是通常情况下,如果你的套接字很少,它不会去WaitOne()线,但是如果你有很多,如果服务器不堪重负,数据就会松动?把类似的东西放在那里不是一个聪明的举动,'等到我处理这个套接字,否则拒绝其他套接字'并且客户端就像if i was rejected, i will try over 100 miliseconds again?? 3-> 我在哪里看到这条消息? A request to send ... ??非常感谢您的回复!
  • @Damian 1) 使用Reset()。 2)问题(至少在我运行它时)不是数据丢失,而是数据从未发送,因为客户端试图在套接字准备好之前发送数据(因为connectDone.WaitOne() 从未等待)。 3) 这些消息被写入控制台窗口。
  • 1-> 我把这个Reset() 方法放在哪里? 2->所以问题一定出在客户端,对吧? 3-> 当我测试这个时,我在没有visual studio 2012 的 Windows 7 上测试,我可以在其他地方看到这个日志吗?谢谢。
  • 编辑:查看我的日志pastebin.com/cKwbeh5W,这是我在套接字被拒绝时得到的结果
  • @Damian 这是AutoResetEvent
猜你喜欢
  • 2011-03-15
  • 2020-02-22
  • 1970-01-01
  • 2021-06-20
  • 1970-01-01
  • 2014-03-12
  • 2012-11-12
  • 1970-01-01
  • 2011-01-22
相关资源
最近更新 更多