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

时间:2016-09-26 18:32:52

标签: c# sockets

我正在启动套接字程序,我正在设置服务器和两种类型的客户端(请求者和仲裁者)。我正在测试连接,但它们并不是很有效。现在我只为每个表单设置一个按钮:Arbiter的“Accept”按钮和Requester的“Request”按钮。每个按钮都应该在另一个窗体上弹出一个弹出窗口,但两个窗口都不起作用。另外,我注意到当我关闭所有程序时,服务器仍然在我的进程中运行。我做错了什么?

以下是服务器代码:

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

Arbiter Class:

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

这是我第一次进入套接字编程。

2 个答案:

答案 0 :(得分:0)

首先从上一个问题开始,除非您指定超时,否则接收将阻止,直到数据可用。由于您的线程是前台线程,这将阻止您的应用程序终止。见https://msdn.microsoft.com/en-us/library/8s4y8aff(v=vs.110).aspx。使用超时,和/或使您的线程后台线程导致它们在您关闭应用程序的主线程时终止。将创建的线程的IsBackground属性设置为true以实现此目的。 (另外,在上面的文章中注意关于Shutdown的段落和Receive方法返回一个空数组。这是你提示优雅地关闭你身边的连接。)

TCP / IP堆栈将自行决定发送数据(Nagle的算法),这意味着您偶尔会收到包含多条或部分消息的缓冲区。由于您的线程中存在“静默”错误处理,因此可能由于消息损坏而导致您的线程过早终止?将收到的所有内容放在缓冲区中,并在将缓冲区传递给消息处理程序之前,在单独的步骤/线程中检查缓冲区中是否有完整的消息。

这里没有明确的答案我很害怕,但如果检查损坏的消息没有帮助,请查看MSDN上的Socket示例。这可能只是你遗漏的一小部分细节。

答案 1 :(得分:0)

您正在制作一个基本且常见的TCP错误。 TCP是面向字节的流协议,而不是面向消息的。您的接收代码假定它在读取时收到一个Packet。这不保证,您可能会收到1个字节,20个字节或其他任何内容。您必须循环接收,直到您收到所有消息。这意味着您必须知道何时阅读了所有内容。要么最后需要一个标题或一些标记。