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();
}
}