在C#中创建一个线程安全的套接字类

时间:2014-06-02 19:17:11

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

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

计算“MyComputer-PC \ user1”=&gt; 8903

计算“MyComputer-PC \ user2”=&gt; 8464

计算“MyComputer-PC \ user3”=&gt; 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

谢谢。

1 个答案:

答案 0 :(得分:2)

问题似乎是在客户端中使用ManualResetEvent s。请注意,ManualResetEvent必须手动重置,否则事件WaitOne()后的所有Set()次呼叫都会立即返回。因此,在发送第一条消息后尝试发送数据之前,您的客户端不会等待套接字连接,如我在计算机上运行时看到的以下消息所示:

  

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

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

相关问题