C#中Socket编程,异步实现Server端定时发送消息

在最近项目需求中,要求服务端定时向客服端发送消息。由于客户端从机的特性,只能接收Server发送的消息后回复,不能主动向服务端发送消息。

起初,并未使用异步的方法进行编程,使用了Accept()、Revice()等方法。由于从机不能主动发送消息的特性,并未考虑到从机断电不能接收到Server消息的情况,Server会因为没有消息返回导致线程阻塞,不能继续轮询发送。

之后,对程序进行改进,用异步的方式解决了问题。

Server端代码Demo:

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

public class NonBlockingServer
{
    private const int bufferSize = 1024;
    private byte[] sendBuffer = Encoding.UTF8.GetBytes("Hello, client!"); // Data to send to clients
    private byte[] receiveBuffer = new byte[bufferSize]; // Data buffer to receive from clients
    private Socket serverSocket;
    private Timer timer;
    private int sendCounter = 0;
    private List<Socket> connectedClients = new List<Socket>(); // List to store connected client sockets

    public void StartServer()
    {
        // Create a TCP/IP socket.
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        // Bind the socket to the local endpoint and start listening for incoming connections.
        IPAddress ipAddress = IPAddress.Any; // Listen on all available interfaces
        int port = 12345; // Choose any available port
        IPEndPoint localEP = new IPEndPoint(ipAddress, port);

        serverSocket.Bind(localEP);
        serverSocket.Listen(10);

        Console.WriteLine("Server started. Waiting for clients...");

        // Start the timer to periodically send data to connected clients.
        int intervalMilliseconds = 2000; // 2 seconds
        timer = new Timer(TimerCallback, null, 0, intervalMilliseconds);

        // Accept incoming connections asynchronously.
        serverSocket.BeginAccept(AcceptCallback, null);
    }

    private void AcceptCallback(IAsyncResult ar)
    {
        try
        {
            // Get the socket that represents the new connection.
            Socket clientSocket = serverSocket.EndAccept(ar);

            // Continue accepting new connections.
            serverSocket.BeginAccept(AcceptCallback, null);

            // Add the new client socket to the list of connected clients.
            connectedClients.Add(clientSocket);

            // Start receiving data from the client asynchronously.
            clientSocket.BeginReceive(receiveBuffer, 0, bufferSize, SocketFlags.None, ReceiveCallback, clientSocket);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error accepting connection: {ex.Message}");
        }
    }

    private void TimerCallback(object state)
    {
        try
        {
            // Send data to all connected clients asynchronously.
            foreach (var clientSocket in connectedClients)
            {
                clientSocket.BeginSend(sendBuffer, 0, sendBuffer.Length, SocketFlags.None, SendCallback, clientSocket);
            }

            // Increment the send counter for tracking purposes.
            sendCounter++;

            // On the 5th transmission, send a special message to the client.
            if (sendCounter == 5)
            {
                foreach (var clientSocket in connectedClients)
                {
                    byte[] specialBuffer = Encoding.UTF8.GetBytes("Special message from server!");
                    clientSocket.BeginSend(specialBuffer, 0, specialBuffer.Length, SocketFlags.None, SendCallback, clientSocket);
                }
                sendCounter = 0; // Reset the counter for the next round.
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error sending data: {ex.Message}");
        }
    }

    private void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            // Get the socket that represents the client.
            Socket clientSocket = (Socket)ar.AsyncState;

            // End the receive operation and get the number of bytes received.
            int bytesRead = clientSocket.EndReceive(ar);

            if (bytesRead > 0)
            {
                // Process the received data (assuming it's text).
                string receivedData = Encoding.UTF8.GetString(receiveBuffer, 0, bytesRead);
                Console.WriteLine($"Received data from client: {receivedData}");

                // Continue receiving more data.
                clientSocket.BeginReceive(receiveBuffer, 0, bufferSize, SocketFlags.None, ReceiveCallback, clientSocket);
            }
            else
            {
                // If no data is received, the client closed the connection.
                Console.WriteLine("Connection closed by the client.");
                connectedClients.Remove(clientSocket);
                clientSocket.Close();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error receiving data: {ex.Message}");
        }
    }

    private void SendCallback(IAsyncResult ar)
    {
        try
        {
            // End the send operation.
            Socket clientSocket = (Socket)ar.AsyncState;
            int bytesSent = clientSocket.EndSend(ar);
            Console.WriteLine($"Sent {bytesSent} bytes to a client.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error sending data: {ex.Message}");
        }
    }

    public static void Main(string[] args)
    {
        NonBlockingServer server = new NonBlockingServer();
        server.StartServer();

        // Keep the console application running until the user decides to exit.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}

Client端代码Demo:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class NonBlockingClient
{
    private const int bufferSize = 1024;
    private byte[] receiveBuffer = new byte[bufferSize]; // Data buffer to receive from the server
    private Socket clientSocket;

    public void StartClient()
    {
        // Create a TCP/IP socket.
        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        // Set up the endpoint and connect to the server.
        IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); // Replace with the server IP address.
        int port = 12345; // Replace with the server port number.
        IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);

        try
        {
            clientSocket.Connect(remoteEP);
            Console.WriteLine("Connected to the server.");

            // Start receiving data from the server asynchronously.
            clientSocket.BeginReceive(receiveBuffer, 0, bufferSize, SocketFlags.None, ReceiveCallback, null);

            // Prevent the client from exiting immediately.
            // You can remove this line if you want the client to exit immediately after connecting.
            Thread.Sleep(Timeout.Infinite);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
            clientSocket.Close();
        }
    }

    private void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            // End the receive operation and get the number of bytes received.
            int bytesRead = clientSocket.EndReceive(ar);

            if (bytesRead > 0)
            {
                // Process the received data (assuming it's text).
                string receivedData = Encoding.UTF8.GetString(receiveBuffer, 0, bytesRead);
                Console.WriteLine($"Received data from server: {receivedData}");

                // Continue receiving more data.
                clientSocket.BeginReceive(receiveBuffer, 0, bufferSize, SocketFlags.None, ReceiveCallback, null);
            }
            else
            {
                // If no data is received, the server closed the connection.
                Console.WriteLine("Connection closed by the server.");
                clientSocket.Close();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error receiving data: {ex.Message}");
            clientSocket.Close();
        }
    }

    public static void Main(string[] args)
    {
        NonBlockingClient client = new NonBlockingClient();
        client.StartClient();
    }
}

posted @ 2023-07-31 22:03  譬嘶  阅读(380)  评论(1编辑  收藏  举报