在同一台电脑上同时TCP客户端和服务器



我正在尝试用C#实现一个基于tcp的小型应用程序。我是TCP编程的新手。

这个应用程序真的很简单。这是一个客户端/服务器场景,最多支持2个客户端。一个客户端将是"外部"IP,另一个将是主机本身。

每当客户端连接到服务器时,它就开始从服务器接收数据包并向服务器发送数据包。

我已经建立了一个小型控制台应用程序,它实现了TCP服务器端代码和TCP客户端代码。

服务器使用绑定到特定端口的侦听器,并等待客户端连接。一旦连接了客户端,它就会生成一个新线程来处理与客户端的通信。

客户端只需连接到服务器,一旦连接建立,它就开始接收和发送数据包。

正如我之前所说的"主持人也是自己的客户",但这导致了一个问题。如果客户端向服务器发送数据包,则服务器和客户端都声称已接收到数据包。这很正常吗?我是不是错过了什么?

更新2(修复问题)

我想我已经找到问题了。一旦客户端完成发送操作,我需要"重置"缓冲区

case SocketAsyncOperation.Send:                        
// Put in listen mode
// Buffer reset AFTER send and BEFORE receive
_receiver.SetBuffer(new byte[4096], 0, 4096);
_client.ReceiveAsync(_receiver);                        
break;

UPDATE 1(添加代码)

服务器

public void Host()
{                        
_listener = new TcpListener(new IPEndPoint(IPAddress.Any, this.ConnectionPort));
_listener.Start();
this.IsListening = true;            
ThreadPool.QueueUserWorkItem((state) =>
{
while (true)
{
// Blocks until a client connections occours
// If continue with no client then stop listening                    
try
{
TcpClient client = _listener.AcceptTcpClient();
// Add client to peer list
long assignedID = DateTime.UtcNow.Ticks;
_clients.Add(assignedID, client);
HandleClient(assignedID);
if (this.ConnectedClients == this.MaxClients)
{
_listener.Stop();
this.IsListening = false;
break;
}
}
catch
{
// Server has stop listening
_stopListen.Set();
break;
}
}
});
}
private void HandleClient(long assignedID)
{
ThreadPool.QueueUserWorkItem((state) =>
{
long clientID = (long)state;
TcpClient client = _clients[clientID];        
try
{
byte[] message = new byte[4096];
byte[] buffer = new byte[4096];
int readed = 0;
int index = 0;
NetworkStream clientStream = client.GetStream();
while (true)
{                        
readed = clientStream.Read(message, 0, message.Length);
if (readed == 0)
{
// Client disconnected, thorw exception
throw new SocketException();
}
Array.Copy(message, 0, buffer, index, readed);
index += readed;
if (readed == 4096
&& !_packetHub.IsValidBufferData(buffer))
{
Array.Clear(buffer, 0, buffer.Length);
readed = 0;
index = 0;
continue;
}
if (_packetHub.IsValidBufferData(buffer))
{
NetPacket receivedPacket = _packetHub.DeserializePacket(buffer);
NetPacket answerPacket = null;
// Assign sender ID in case of unknown sender ID
if (receivedPacket.SenderID == NetPacket.UnknownID)
receivedPacket.SenderID = clientID;
// Handle received packet and forward answer packet
// to the client or to all other connected peers                            
answerPacket = HandlePacket(receivedPacket);                            
if (answerPacket != null)
{                                
if (answerPacket.RecipientID == clientID)
{
// Send answer packet to the client
SendPacket(client, answerPacket);
}
else
{
// Broadcast packet to all clients
foreach (TcpClient peer in _clients.Values)
SendPacket(peer, answerPacket);
}
}
// Reset receive packet buffer
Array.Clear(buffer, 0, buffer.Length);
readed = 0;
index = 0;
}
}
}
catch
{
System.Diagnostics.Debug.WriteLine("Network error occours!");
}
finally
{
// Close client connection
client.Close();
// Remove the client from the list
_clients.Remove(clientID);
// Raise client disconnected event
OnClientDisconnected();
}
}, assignedID);
}

客户端

public void Connect()
{
if (this.ConnectionAddress == null)
throw new InvalidOperationException("Unable to connect. No server IP specified.");
_receiver = new SocketAsyncEventArgs();
_receiver.RemoteEndPoint = new IPEndPoint(this.ConnectionAddress, this.ConnectionPort);
_receiver.SetBuffer(new byte[4096], 0, 4096);
_receiver.Completed += new EventHandler<SocketAsyncEventArgs>(Socket_OperationComplete);
_client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            
_client.ConnectAsync(_receiver);
}
private void Socket_OperationComplete(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)            
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect:
_readyToSend = true;
this.IsConnected = true;
// Once connection is estabilished, send a join packet to
// retrive server side assigned informations
JoinPacket joinPacket = (JoinPacket)NetPacket.CreatePacket(this.ClientID, NetPacket.ToHost, NetPacket.JoinPacket);
joinPacket.ClientName = this.ClientName;
joinPacket.ClientAddress = this.LocalAddress.ToString();
SendPacket(joinPacket);
break;
case SocketAsyncOperation.Receive:
_readyToSend = true;
byte[] buffer = _receiver.Buffer;
System.Diagnostics.Debug.WriteLine("Client received packet (" + _packetHub.GetBufferDataType(buffer) + ") with length of: " + buffer.Length);
if (_packetHub.IsValidBufferData(buffer))
{
NetPacket receivedPacket = _packetHub.DeserializePacket(buffer);
NetPacket answerPacket = null;
// Handle received packet and forward answer packet
// to the server
answerPacket = HandlePacket(receivedPacket);
if (answerPacket != null)
SendPacket(answerPacket);
}
else
{
_client.ReceiveAsync(_receiver);
}
break;
case SocketAsyncOperation.Send:                        
// Put in listen mode
_client.ReceiveAsync(_receiver);                        
break;
}
}            
else
{
OnClientDisconnected();
System.Diagnostics.Debug.WriteLine(String.Format("Network error: {0}", e.SocketError.ToString()));
}
}

由于使用TCP,因此必须指定一个发送/侦听端口(服务器和客户端不同)。所以,不,这是不正常的(但如果客户端和服务器在同一个端口上发送/侦听,这是正常的)。

最新更新