C# 中套接字标识符的 TCP 套接字服务器自定义 class
TCP Socket Server custom class for socket identifier in C#
我一直在查看许多 tcp client/server 示例,想知道如何创建一种方法来识别每个客户端。我知道的一种方法是通过登录身份验证。我知道如何连接和查询数据库,但是在成功验证后我怎么说,获取用户名并说这个用户名就是这个套接字。 class 示例或简单方法将被视为示例。我希望能够通过数据库中的用户名分别定位所有连接的客户端。
我为服务器使用的示例
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace MultiServer
{
class Program
{
private static readonly Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private static readonly List<Socket> clientSockets = new List<Socket>();
private const int BUFFER_SIZE = 2048;
private const int PORT = 100;
private static readonly byte[] buffer = new byte[BUFFER_SIZE];
static void Main()
{
Console.Title = "Server";
SetupServer();
Console.ReadLine(); // When we press enter close everything
CloseAllSockets();
}
private static void SetupServer()
{
Console.WriteLine("Setting up server...");
serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT));
serverSocket.Listen(0);
serverSocket.BeginAccept(AcceptCallback, null);
Console.WriteLine("Server setup complete");
}
/// <summary>
/// Close all connected client (we do not need to shutdown the server socket as its connections
/// are already closed with the clients).
/// </summary>
private static void CloseAllSockets()
{
foreach (Socket socket in clientSockets)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
serverSocket.Close();
}
private static void AcceptCallback(IAsyncResult AR)
{
Socket socket;
try
{
socket = serverSocket.EndAccept(AR);
}
catch (ObjectDisposedException) // I cannot seem to avoid this (on exit when properly closing sockets)
{
return;
}
clientSockets.Add(socket);
socket.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCallback, socket);
Console.WriteLine("Client connected, waiting for request...");
serverSocket.BeginAccept(AcceptCallback, null);
}
private static void ReceiveCallback(IAsyncResult AR)
{
Socket current = (Socket)AR.AsyncState;
int received;
try
{
received = current.EndReceive(AR);
}
catch (SocketException)
{
Console.WriteLine("Client forcefully disconnected");
// Don't shutdown because the socket may be disposed and its disconnected anyway.
current.Close();
clientSockets.Remove(current);
return;
}
byte[] recBuf = new byte[received];
Array.Copy(buffer, recBuf, received);
string text = Encoding.ASCII.GetString(recBuf);
Console.WriteLine("Received Text: " + text);
if (text.ToLower() == "get time") // Client requested time
{
Console.WriteLine("Text is a get time request");
byte[] data = Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString());
current.Send(data);
Console.WriteLine("Time sent to client");
}
else if (text.ToLower() == "exit") // Client wants to exit gracefully
{
// Always Shutdown before closing
current.Shutdown(SocketShutdown.Both);
current.Close();
clientSockets.Remove(current);
Console.WriteLine("Client disconnected");
return;
}
else
{
Console.WriteLine("Text is an invalid request");
byte[] data = Encoding.ASCII.GetBytes("Invalid request");
current.Send(data);
Console.WriteLine("Warning Sent");
}
current.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCallback, current);
}
}
}
当您使用异步回调时,有一个非常简单的解决方案(尽管我强烈建议切换到更新的方法,或者更好的是,现有的库 - 原始 TCP 很难做到正确)。
回调委托可以指向一个实例方法。这意味着你可以有这样的东西:
class Client
{
private readonly Socket socket;
public readonly byte[] ReceiveBuffer = new byte[BUFFFER_SIZE];
public Client(Socket socket)
{
this.socket = socket;
}
public void ReceiveCallback(IAsyncResult AR)
{
// Handle the received data as usual
}
}
然后在您的 AcceptCallback
方法中,只需使用 Client
列表而不是 Socket
,最后的 BeginReceive
调用如下:
var client = new Client(socket);
socket.BeginReceive(client.ReceiveBuffer, 0, BUFFER_SIZE, SocketFlags.None, client.ReceiveCallback,
socket);
clients.Add(newClient);
但同样,编写自定义网络很难。尽可能使用现有的解决方案 - 有很多选择。
此外,您得到的 ObjectDisposedException
是因为您在 Shutdown
之后紧跟着 Close
。这是错误的。 TCP 关闭是协作式的——在调用套接字上的 Close
之前,您需要等待客户端套接字关闭。您正在做的是在连接有机会自行解决之前粗暴地中断连接。再次重申 - TCP 很难做到正确,您需要非常彻底地了解它的工作原理。
我一直在查看许多 tcp client/server 示例,想知道如何创建一种方法来识别每个客户端。我知道的一种方法是通过登录身份验证。我知道如何连接和查询数据库,但是在成功验证后我怎么说,获取用户名并说这个用户名就是这个套接字。 class 示例或简单方法将被视为示例。我希望能够通过数据库中的用户名分别定位所有连接的客户端。
我为服务器使用的示例
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace MultiServer
{
class Program
{
private static readonly Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private static readonly List<Socket> clientSockets = new List<Socket>();
private const int BUFFER_SIZE = 2048;
private const int PORT = 100;
private static readonly byte[] buffer = new byte[BUFFER_SIZE];
static void Main()
{
Console.Title = "Server";
SetupServer();
Console.ReadLine(); // When we press enter close everything
CloseAllSockets();
}
private static void SetupServer()
{
Console.WriteLine("Setting up server...");
serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT));
serverSocket.Listen(0);
serverSocket.BeginAccept(AcceptCallback, null);
Console.WriteLine("Server setup complete");
}
/// <summary>
/// Close all connected client (we do not need to shutdown the server socket as its connections
/// are already closed with the clients).
/// </summary>
private static void CloseAllSockets()
{
foreach (Socket socket in clientSockets)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
serverSocket.Close();
}
private static void AcceptCallback(IAsyncResult AR)
{
Socket socket;
try
{
socket = serverSocket.EndAccept(AR);
}
catch (ObjectDisposedException) // I cannot seem to avoid this (on exit when properly closing sockets)
{
return;
}
clientSockets.Add(socket);
socket.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCallback, socket);
Console.WriteLine("Client connected, waiting for request...");
serverSocket.BeginAccept(AcceptCallback, null);
}
private static void ReceiveCallback(IAsyncResult AR)
{
Socket current = (Socket)AR.AsyncState;
int received;
try
{
received = current.EndReceive(AR);
}
catch (SocketException)
{
Console.WriteLine("Client forcefully disconnected");
// Don't shutdown because the socket may be disposed and its disconnected anyway.
current.Close();
clientSockets.Remove(current);
return;
}
byte[] recBuf = new byte[received];
Array.Copy(buffer, recBuf, received);
string text = Encoding.ASCII.GetString(recBuf);
Console.WriteLine("Received Text: " + text);
if (text.ToLower() == "get time") // Client requested time
{
Console.WriteLine("Text is a get time request");
byte[] data = Encoding.ASCII.GetBytes(DateTime.Now.ToLongTimeString());
current.Send(data);
Console.WriteLine("Time sent to client");
}
else if (text.ToLower() == "exit") // Client wants to exit gracefully
{
// Always Shutdown before closing
current.Shutdown(SocketShutdown.Both);
current.Close();
clientSockets.Remove(current);
Console.WriteLine("Client disconnected");
return;
}
else
{
Console.WriteLine("Text is an invalid request");
byte[] data = Encoding.ASCII.GetBytes("Invalid request");
current.Send(data);
Console.WriteLine("Warning Sent");
}
current.BeginReceive(buffer, 0, BUFFER_SIZE, SocketFlags.None, ReceiveCallback, current);
}
}
}
当您使用异步回调时,有一个非常简单的解决方案(尽管我强烈建议切换到更新的方法,或者更好的是,现有的库 - 原始 TCP 很难做到正确)。
回调委托可以指向一个实例方法。这意味着你可以有这样的东西:
class Client
{
private readonly Socket socket;
public readonly byte[] ReceiveBuffer = new byte[BUFFFER_SIZE];
public Client(Socket socket)
{
this.socket = socket;
}
public void ReceiveCallback(IAsyncResult AR)
{
// Handle the received data as usual
}
}
然后在您的 AcceptCallback
方法中,只需使用 Client
列表而不是 Socket
,最后的 BeginReceive
调用如下:
var client = new Client(socket);
socket.BeginReceive(client.ReceiveBuffer, 0, BUFFER_SIZE, SocketFlags.None, client.ReceiveCallback,
socket);
clients.Add(newClient);
但同样,编写自定义网络很难。尽可能使用现有的解决方案 - 有很多选择。
此外,您得到的 ObjectDisposedException
是因为您在 Shutdown
之后紧跟着 Close
。这是错误的。 TCP 关闭是协作式的——在调用套接字上的 Close
之前,您需要等待客户端套接字关闭。您正在做的是在连接有机会自行解决之前粗暴地中断连接。再次重申 - TCP 很难做到正确,您需要非常彻底地了解它的工作原理。