套接字-不发送/接收数据

本文关键字:数据 套接字 | 更新日期: 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字节或其他。你必须循环接收,直到你得到所有的一条消息。这意味着你必须知道你什么时候读完了。要么有一个标题,要么在结尾有一些哨兵