套接字-不发送/接收数据
本文关键字:数据 套接字 | 更新日期: 2023-09-27 18:09:06
我正在启动一个套接字程序,并且正在设置服务器和两种类型的客户端(请求者和仲裁者)。我正在测试这些连接,但它们不太好用。现在每个表单都有一个按钮:仲裁者有一个"接受"按钮,请求者有一个"请求"按钮。每个按钮都应该在另一个表单上弹出,但两者都不起作用。此外,我注意到,当我关闭所有程序时,服务器仍在我的进程中运行。我做错了什么?
下面是服务器代码:
namespace FPPLNotificationServer
{
class Server
{
static Socket listenerSocket;
static List<ClientData> _clients;
static void Main(string[] args)
{
Console.WriteLine("Starting server on " + Packet.GetIP4Address());
listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clients = new List<ClientData>();
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(Packet.GetIP4Address()), 4242);
listenerSocket.Bind(ip);
Thread listenThread = new Thread(ListenThread);
listenThread.Start();
}
static void ListenThread()
{
for (;;)
{
listenerSocket.Listen(0);
_clients.Add(new ClientData(listenerSocket.Accept()));
}
}
public static void Data_IN(object cSocket)
{
Socket clientSocket = (Socket)cSocket;
byte[] Buffer;
int readBytes;
for (;;)
{
try
{
Buffer = new byte[clientSocket.SendBufferSize];
readBytes = clientSocket.Receive(Buffer);
if(readBytes > 0)
{
Packet packet = new Packet(Buffer);
DataManager(packet);
}
}catch(SocketException ex)
{
Console.WriteLine("Client Disconnected");
}
}
}
public static void DataManager(Packet p)
{
switch (p.packetType)
{
case Packet.PacketType.Notification:
foreach(ClientData c in _clients)
{
c.clientSocket.Send(p.ToBytes());
}
break;
}
}
}
class ClientData
{
public Socket clientSocket;
public Thread clientThread;
public string id;
public ClientData()
{
this.id = Guid.NewGuid().ToString();
clientThread = new Thread(Server.Data_IN);
clientThread.Start(clientSocket);
SendRegistrationPacket();
}
public ClientData(Socket clientSocket)
{
this.clientSocket = clientSocket;
this.id = Guid.NewGuid().ToString();
clientThread = new Thread(Server.Data_IN);
clientThread.Start(clientSocket);
SendRegistrationPacket();
}
public void SendRegistrationPacket()
{
Packet p = new Packet(Packet.PacketType.Registration, "server");
p.Gdata.Add(id);
clientSocket.Send(p.ToBytes());
}
}
}
ServerData
namespace FPPLNotificationServerData
{
[Serializable]
public class Packet
{
public List<String> Gdata;
public int packetInt;
public bool packetBool;
public string senderID;
public PacketType packetType;
public string PlantName, ProductSegment, ProductCustomer;
public int PlantNumber;
public string ProductNumber, ProductAltNumber;
public string ProductDiscription;
public int ProductLine;
public string ProductClass, ProductLocation;
public int ProductMcDFactor;
public Packet(PacketType type, String senderID)
{
Gdata = new List<string>();
this.senderID = senderID;
this.packetType = type;
}
public Packet(byte[] packetBytes)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream(packetBytes);
Packet p = (Packet)bf.Deserialize(ms);
ms.Close();
this.Gdata = p.Gdata;
this.senderID = p.senderID;
this.packetType = p.packetType;
this.packetBool = p.packetBool;
this.packetInt = p.packetInt;
}
public byte[] ToBytes()
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, this);
byte[] bytes = ms.ToArray();
ms.Close();
return bytes;
}
public static string GetIP4Address()
{
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
foreach(IPAddress i in ips)
{
if(i.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
return i.ToString();
}
}
return "127.0.0.1";
}
public enum PacketType
{
Registration,
Chat,
Notification,
Request,
ArbiterDecision,
Accept,
Decline
}
}
}
请求类:
namespace FPPLRequestClient
{
public partial class frm_Request : Form
{
public static Socket master;
public static string name;
public static string id;
public bool isConnected;
public frm_Request()
{
InitializeComponent();
string IP = "127.0.0.1";
master = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipEP = new IPEndPoint(IPAddress.Parse(IP), 4242);
try
{
master.Connect(ipEP);
isConnected = true;
}
catch (Exception)
{
isConnected = false;
}
string connectionStatus = isConnected ? "Connected" : "Disconnected";
this.lbl_Status.Text = "Status: " + connectionStatus;
Thread t = new Thread(Data_IN);
t.Start();
}
void Data_IN()
{
byte[] Buffer;
int readBytes;
while (isConnected)
{
try
{
Buffer = new byte[master.SendBufferSize];
readBytes = master.Receive(Buffer);
if(readBytes > 0)
{
DataManager(new Packet(Buffer));
}
}catch(SocketException ex)
{
isConnected = false;
this.Dispose();
}
}
}//END DATA IN
void DataManager(Packet p)
{
switch (p.packetType)
{
case Packet.PacketType.Registration:
id = p.Gdata[0];
break;
case Packet.PacketType.Accept:
//MessageBox.Show(p.ProductNumber);
this.lbl_Status.Text = p.ProductNumber + " accepted";
Invalidate();
break;
}
}
private void btn_Request_Click(object sender, EventArgs e)
{
Packet p = new Packet(Packet.PacketType.Request, id);
p.ProductNumber = "123456";
master.Send(p.ToBytes());
}
}
}
仲裁者类:
namespace FPPLArbiterClient
{
public partial class frm_Arbiter : Form
{
public static Socket master;
public static string name;
public static string id;
public bool isConnected;
public frm_Arbiter()
{
InitializeComponent();
string IP = "127.0.0.1";
master = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipEP = new IPEndPoint(IPAddress.Parse(IP), 4242);
try
{
master.Connect(ipEP);
isConnected = true;
}
catch (Exception)
{
isConnected = false;
}
string connectionStatus = isConnected ? "Connected" : "Disconnected";
this.lbl_Status.Text = "Status: " + connectionStatus;
Thread t = new Thread(Data_IN);
t.Start();
}
void Data_IN()
{
byte[] Buffer;
int readBytes;
while (isConnected)
{
try
{
Buffer = new byte[master.SendBufferSize];
readBytes = master.Receive(Buffer);
if(readBytes > 0)
{
DataManager(new Packet(Buffer));
}
}catch(SocketException ex)
{
isConnected = false;
this.Dispose();
}
}
}//END DATA IN
void DataManager(Packet p)
{
switch (p.packetType)
{
case Packet.PacketType.Registration:
id = p.Gdata[0];
break;
case Packet.PacketType.Request:
MessageBox.Show(p.ProductNumber + " Requested from " + p.senderID);
break;
}
}
private void btn_Accept_Click(object sender, EventArgs e)
{
MessageBox.Show("Sending acceptance of 126456");
Packet p = new Packet(Packet.PacketType.Accept, id);
p.ProductNumber = "123456";
master.Send(p.ToBytes());
}
}
}
首先从您的最后一个问题开始,Receive将阻塞直到数据可用,除非您指定超时。由于您的线程是前台线程,这将防止应用程序终止。见https://msdn.microsoft.com/en-us/library/8s4y8aff (v = vs.110) . aspx。要么使用超时,要么将线程设置为后台线程,导致它们在关闭应用程序的主线程时终止。将创建的线程的IsBackground属性设置为true。(另外,在上面的文章中,请注意关于Shutdown和Receive方法返回空数组的段落。这是在你这边优雅地关闭连接的提示)。
TCP/IP栈将根据自己的判断(Nagle算法)发送数据,这意味着你偶尔会收到一个包含几个或部分消息的缓冲区。由于您在线程中有"静默"错误处理,也许您的线程由于损坏的消息而过早终止?将接收到的所有内容放在缓冲区中,并在将其传递给消息处理程序之前,在单独的步骤/线程中检查缓冲区中的完整消息。
恐怕这里没有明确的答案,但如果检查损坏的消息没有帮助,请查看MSDN上的套接字示例。
你正在犯一个基本的和常见的TCP错误。TCP是面向字节的流协议,而不是面向消息的。您的接收代码假设它在读取时接收到一个Packet
。这不能保证,您可能会收到1字节、20字节或其他。你必须循环接收,直到你得到所有的一条消息。这意味着你必须知道你什么时候读完了。要么有一个标题,要么在结尾有一些哨兵