每次重新使用套接字或创建新套接字?

时间:2016-02-20 16:25:04

标签: c# sockets unreal-engine4

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

this(虚幻引擎)和this(C#)为参考,我成功地将来自C#的消息发送给了UE。

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

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

如果两种选择都可行,那么在任何方面都有明显的优势/劣势吗?

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

感谢任何帮助或评论。

1 个答案:

答案 0 :(得分:0)

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#Console应用程序进行测试的可行解决方案。您还可以看到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());
            }
        }
    }
}