【问题标题】:Reuse sockets or create new one every time?重复使用套接字还是每次都创建一个新的?
【发布时间】:2016-06-02 05:41:35
【问题描述】:

我有 2 个应用程序在需要通信的同一台机器上运行。一个是用虚幻引擎制作的,另一个是 C# 桌面应用程序。

this(虚幻引擎)和this(C#)为参考,我成功地从C#向UE发送了一条消息。

问题是我只能发送一条消息;第二次我没有收到错误,但没有收到消息。

解决此问题的正确方法是关闭套接字并为每条消息创建一个新套接字,还是重用同一个套接字?

如果这两种选择都可行,是否有任何明显的优势/劣势?

作为参考,在我的特定场景中,通信只是一种方式(C# 到 UE),发送的消息每 10-60 秒只有几个字节。

感谢任何帮助或 cmets。

【问题讨论】:

    标签: c# sockets unreal-engine4


    【解决方案1】:

    code example on C# 中,显示套接字仅用于连接、发送和接收一次(并且所有操作都是同步完成的——也就是说,除非方法调用是完成)在关闭之前:

    sender.Connect(remoteEP);
    
    Console.WriteLine("Socket connected to {0}",
        sender.RemoteEndPoint.ToString());
    
    // Encode the data string into a byte array.
    byte[] msg = Encoding.ASCII.GetBytes("This is a test<EOF>");
    
    // Send the data through the socket.
    int bytesSent = sender.Send(msg);
    
    // Receive the response from the remote device.
    int bytesRec = sender.Receive(bytes);
    Console.WriteLine("Echoed test = {0}",
        Encoding.ASCII.GetString(bytes,0,bytesRec));
    
    // Release the socket.
    sender.Shutdown(SocketShutdown.Both);
    sender.Close();
    

    因此,如果您在虚幻引擎中的 TCP Socket 侦听器没有关闭当前连接,您将失去与它连接的特定套接字连接,因此无法继续进行。

    此外,请注意使用同步连接、发送和接收的示例代码,这几乎肯定会阻塞您的代码,并且在大多数情况下并不理想。

    所以,不要只是简单地使用示例代码,你需要对其进行大量修改。

    我建议您查看this post,其中包含使用Socket 类进行TCP/IP 通信的服务器和客户端的代码。在帖子中,已经有一个使用 C# 控制台应用程序进行测试的有效解决方案。您还可以看到Async(而不是Sync)如何用于ConnectReceive

    这里是代码的摘录(只需更改 SERVER_IPPORT_NO 以适合您的,并且您可能只需要使用客户端,因为在您的情况下,虚幻引擎是服务器):

    测试代码:

    服务器

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace TcpListenerConsoleApplication {
        class Program {
            const int PORT_NO = 2201;
            const string SERVER_IP = "127.0.0.1";
            static Socket serverSocket;
            static void Main(string[] args) {
                //---listen at the specified IP and port no.---
                Console.WriteLine("Listening...");
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
                serverSocket.Listen(4); //the maximum pending client, define as you wish
                serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null);      
                string result = "";
                do {
                    result = Console.ReadLine();
                } while (result.ToLower().Trim() != "exit");
            }
    
            private const int BUFFER_SIZE = 4096;
            private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
            private static void acceptCallback(IAsyncResult result) { //if the buffer is old, then there might already be something there...
                Socket socket = null;
                try {
                    socket = serverSocket.EndAccept(result); // The objectDisposedException will come here... thus, it is to be expected!
                    //Do something as you see it needs on client acceptance
                    socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                    serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
                } catch (Exception e) { // this exception will happen when "this" is be disposed...        
                    //Do something here             
                    Console.WriteLine(e.ToString());
                }
            }
    
            const int MAX_RECEIVE_ATTEMPT = 10;
            static int receiveAttempt = 0; //this is not fool proof, obviously, since actually you must have multiple of this for multiple clients, but for the sake of simplicity I put this
            private static void receiveCallback(IAsyncResult result) {
                Socket socket = null;
                try {
                    socket = (Socket)result.AsyncState; //this is to get the sender
                    if (socket.Connected) { //simple checking
                        int received = socket.EndReceive(result);
                        if (received > 0) {
                            byte[] data = new byte[received]; //the data is in the byte[] format, not string!
                            Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to http://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
                            //DO SOMETHING ON THE DATA int byte[]!! Yihaa!!
                            Console.WriteLine(Encoding.UTF8.GetString(data)); //Here I just print it, but you need to do something else                     
    
                            //Message retrieval part
                            //Suppose you only want to declare that you receive data from a client to that client
                            string msg = "I receive your message on: " + DateTime.Now;                      
                            socket.Send(Encoding.ASCII.GetBytes(msg)); //Note that you actually send data in byte[]
                            Console.WriteLine("I sent this message to the client: " + msg);
    
                            receiveAttempt = 0; //reset receive attempt
                            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
                        } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //fail but not exceeding max attempt, repeats
                            ++receiveAttempt; //increase receive attempt;
                            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
                        } else { //completely fails!
                            Console.WriteLine("receiveCallback fails!"); //don't repeat beginReceive
                            receiveAttempt = 0; //reset this for the next connection
                        }
                    }
                } catch (Exception e) { // this exception will happen when "this" is be disposed...
                    Console.WriteLine("receiveCallback fails with exception! " + e.ToString());
                }
            }
    
        }
    }
    

    客户

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace TcpClientConsoleApplication {
        class Program {
            const int PORT_NO = 2201;
            const string SERVER_IP = "127.0.0.1";
            static Socket clientSocket; //put here
            static void Main(string[] args) {
                //Similarly, start defining your client socket as soon as you start. 
                clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                loopConnect(3, 3); //for failure handling
                string result = "";
                do {
                    result = Console.ReadLine(); //you need to change this part
                    if (result.ToLower().Trim() != "exit") {
                        byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string
                        //do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes
                        clientSocket.Send(bytes);
                    }
                } while (result.ToLower().Trim() != "exit");
            }
    
            static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
                int attempts = 0;
                while (!clientSocket.Connected && attempts < noOfRetry) {
                    try {
                        ++attempts;
                        IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnectCallback, null);
                        result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
                        System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
                    } catch (Exception e) {
                        Console.WriteLine("Error: " + e.ToString());
                    }
                }
                if (!clientSocket.Connected) {
                    Console.WriteLine("Connection attempt is unsuccessful!");
                    return;
                }
            }
    
            private const int BUFFER_SIZE = 4096;
            private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
            private static void endConnectCallback(IAsyncResult ar) {
                try {
                    clientSocket.EndConnect(ar);
                    if (clientSocket.Connected) {
                        clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
                    } else {
                        Console.WriteLine("End of connection attempt, fail to connect...");
                    }
                } catch (Exception e) {
                    Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
                }
            }
    
            const int MAX_RECEIVE_ATTEMPT = 10;
            static int receiveAttempt = 0;
            private static void receiveCallback(IAsyncResult result) {
                System.Net.Sockets.Socket socket = null;
                try {
                    socket = (System.Net.Sockets.Socket)result.AsyncState;
                    if (socket.Connected) {
                        int received = socket.EndReceive(result);
                        if (received > 0) {
                            receiveAttempt = 0;
                            byte[] data = new byte[received];
                            Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to http://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
                            //DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET!
                            //Notice that your data is not string! It is actually byte[]
                            //For now I will just print it out
                            Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
                            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                        } else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again
                            ++receiveAttempt;
                            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
                        } else { //completely fails!
                            Console.WriteLine("receiveCallback is failed!");
                            receiveAttempt = 0;
                            clientSocket.Close();
                        }
                    }
                } catch (Exception e) { // this exception will happen when "this" is be disposed...
                    Console.WriteLine("receiveCallback is failed! " + e.ToString());
                }
            }
        }
    }
    

    【讨论】:

    • 我已将client code 更改为循环发送和接收(同步),但我每次都在创建和销毁套接字。 Socket Listener 没有关闭 Unreal 上的连接,所以我失去了连接,这就是为什么只有第一条消息通过。感谢您的回答
    • @jmcorallo 我明白了......很高兴我的回答可以提供任何帮助。祝你的项目好运! ;)
    猜你喜欢
    • 2016-05-07
    • 2014-08-14
    • 2015-09-23
    • 1970-01-01
    • 1970-01-01
    • 2017-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多