在c#中创建线程安全套接字类

本文关键字:安全套 套接字 安全 线程 创建 | 更新日期: 2023-09-27 18:06:22

我对SocketsThread Safe插座的一些概念有一些疑问。我有两个类,一个服务器类和一个客户端类:

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个客户端停止后,我在' notepad++ '中打开日志文件,并计算以下结果:

count "MyComputer-PC'user1" => 8903

count "MyComputer-PC'user2" => 8464

count "MyComputer-PC'user3" => 8990

为什么?一些数据已经丢失,它应该呈现10.000 10.000 10.000…

我该如何解决这个问题?

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

编辑

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

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

谢谢。

在c#中创建线程安全套接字类

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

发送或接收数据的请求被拒绝,因为套接字未连接并且(当使用sendto调用在数据报套接字上发送时)没有提供地址

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