简体   繁体   中英

The data may need to be resent through Socket if no acknowledgement has been received in time from the Gateway server

The below code is used to send the data to the gateway sever and in response receives an acknowledgement. But sometimes the acknowledgement is not received in which case I think the request has to be sent again. The below code surely does not handle that part. I would like to know any possible solution to this problem.

class TestSMS
    {
        public static string strIp = "10.00.105.00";
        public static int Port = 1009;
        //-----------------connect------------------//


        public static Socket Connect(string host, int port)
        {
            Socket socket = new Socket(AddressFamily.InterNetwork,
                SocketType.Stream,
                ProtocolType.Tcp);

            Console.WriteLine("Establishing Connection to {0}",
                host);
            socket.Connect(host, port);
            clsError.LogError("TestSMS:Connect()", "Connection established");
            return socket;
        }



        //---------------End connect----------------//

        //-------------------send--------------------//



        public static int SendReceiveTest(Socket server,string Message)
        {
            byte[] connectionString = Encoding.UTF8.GetBytes("CON^`!XML^`!ABCD#END#");
            byte[] msg = Encoding.UTF8.GetBytes(Message);
            byte[] bytes = new byte[1024];
            string response = string.Empty;
            int byteCount = 0;
            int i = 0;
            try
            {
                // Blocks until send returns.
                 i = server.Send(connectionString, connectionString.Length, SocketFlags.None);
                clsError.LogError("TestSMS:SendReceiveTest()", "Connection string sent");

                // Get reply from the server.
                 byteCount = server.Receive(bytes, server.Available,
                                                   SocketFlags.None);
                 if (byteCount > 0)
                 {
                     response = Encoding.UTF8.GetString(bytes);
                     if (response.IndexOf("CON!0") != 0)
                     {
                          i = server.Send(msg, msg.Length, SocketFlags.None);

                         clsError.LogError("TestSMS:SendReceiveTest()", "Message data sent");
                         byteCount = server.Receive(bytes, server.Available,
                                                   SocketFlags.None);
                         response = Encoding.UTF8.GetString(bytes);

                         if ((response.ToString().IndexOf("ACK!1") == 0) || (response.ToString().IndexOf("ACK!MSGSTATUS=TRUE") == 0))
                         {
                             clsError.LogError("TestSMS:SendReceiveTest()", "Message sent successfully:  " + response);
                         }
                         //log

                     }
                     else
                     {
                         clsError.LogError("TestSMS:SendReceiveTest()", "Could not handshake with SMS Server");
                     }

                 }
                 else
                 {
                     clsError.LogError("TestSMS:SendReceiveTest()", "No Acknowledgement recieved");
                 }
            }
            catch (SocketException e)
            {
                clsError.LogError("TestSMS:SendReceiveTest()", e.Message.ToString());
                return (e.ErrorCode);
            }
            return 0;
        }



        //-----------------End send------------------//

        static void Main(string[] args)
        {  

            try
            {
                string strXmlFormat = " MSG^`!<DEPT>IPL</DEPT><APPID>TLC</APPID><MOBILE>918879440021</MOBILE><DEPTMSGID>14092111115JH21075</DEPTMSGID><MESSAGE>This is a test message</MESSAGE><FROMDATETIME></FROMDATETIME><TODATETIME></TODATETIME><NODELIVERYTIMEFROM>2200</NODELIVERYTIMEFROM><NODELIVERYTIMETO>0700</NODELIVERYTIMETO><HTTPMODE>S</HTTPMODE><REMARKS></REMARKS><REMARKS1></REMARKS1><REMARKS2></REMARKS2><TRN_GENERATE_TIMESTAMP>2015-12-10 16:48:54 </TRN_GENERATE_TIMESTAMP>#END#";
                clsError.LogError("Inside Main:Msg in XML format", strXmlFormat);
                Socket Socket;
                Socket = TestSMS.Connect(TestSMS.strIp, TestSMS.Port);

                TestSMS.SendReceiveTest(Socket, strXmlFormat);


            }
            catch (Exception ex)
            {
                clsError.LogError("Inside Main:Main()", ex.Message.ToString());
                throw ex;

            }

        }



    }

Problem:

Your main issue is in the server.Receive statements (there are two of them)

 byteCount = server.Receive(bytes, server.Available, SocketFlags.None); //only read when byte is available

This statement itself will be passed as soon as there is a reply coming from the socket but not necessarily after ALL data have been received.

Here is what I capture by simulating the procedure in my PC:

在此输入图像描述

  1. In the above picture, the program is running until the server.Receive line, but waiting for the response. Thus it does not go to the if (byteCount > 0) line. The Form at the right being my simulated gateway server. It shows that it receives the message.

在此输入图像描述

  1. In the picture above, I sent reply "DummyReply" to the client. And the program runs until the line if (byteCount > 0) but, look at that(!), the byteCount is zero! This is the core of the error. And I did a loop for SendReceiveTest and see what happened next...

在此输入图像描述

  1. In the right Form it is showed that the client sends another test message and the server hasn't given any reply to it but it goes directly to the if (byteCount > 0) line and see the byteCount value. It is 10 (This is "DummyReply" message length!), which is the previous reply I sent from the server!

Thus, it is clear that the server.Receive creates problem for your system.

Take a deep breath...

Solution:

I personally might be tempted to solve this problem with ASync rather than Sync , but granting the possibility that your procedure here could not be solved with ASync for one reason or another, here is what I will do based on your code:

//-------------------send--------------------//
public static int SendReceiveTest(Socket server, string Message) {
    byte[] connectionString = Encoding.UTF8.GetBytes("CON^`!XML^`!ABCD#END#");
    byte[] msg = Encoding.UTF8.GetBytes(Message);
    byte[] bytes = new byte[1024];
    string response = string.Empty;
    int byteCount = 0;
    int i = 0;
    try {
        i = server.Send(connectionString, connectionString.Length, SocketFlags.None); // Blocks until send returns.
        clsError.LogError("TestSMS:SendReceiveTest()", "Connection string sent");

        // Get reply from the server.
        int retryCount = 0, retryLimit = 5; //declare these two new variables
        while (retryCount < retryLimit) { //repeats the waiting as long as it is within the retry limit
            if (server.Available > 0) //check for the available byte first, please change 0 with number of bytes which you expected
                byteCount = server.Receive(bytes, server.Available, SocketFlags.None); //only read when byte is available
            retryCount++;
            //Log the retry count here if your wish: clsError.LogError("Retry Count: " + retryCount.ToString(), "Retry attempt to get response");
            if (byteCount > 0 && retryCount < retryLimit) //if there is byte received at this point or condition violated, break
                break;
            System.Threading.Thread.Sleep(1000); //no byte received re-check in another second;
        }

        if (byteCount <= 0) { // no byte received
            clsError.LogError("TestSMS:SendReceiveTest()", "No Acknowledgement recieved");
            return -1; //change this with your own error code
        } //passing this point means you receive something
        response = Encoding.UTF8.GetString(bytes);
        if (response.IndexOf("CON!0") == 0) {
            clsError.LogError("TestSMS:SendReceiveTest()", "Could not handshake with SMS Server");
            return -2; //change this with your own error code
        } //passing this points means response.IndexOf("CON!0") != 0

        retryCount = 0; //reset retry count, change retryLimit as per necessary if needed be
        byteCount = 0; //reset byte count
        i = server.Send(msg, msg.Length, SocketFlags.None); //"Message data sent"

        while(retryCount < retryLimit) { //similar concept with above
            if (server.Available > 0) //check for the available byte first, please change 0 with number of bytes which you expected
                byteCount = server.Receive(bytes, server.Available, SocketFlags.None); //only read when byte is available
            //Log the retry count here if your wish: clsError.LogError("Retry Count: " + retryCount.ToString(), "Retry attempt to get message");
            retryCount++;
            if (byteCount > 0 && retryCount < retryLimit) //if there is byte received at this point or condition violated, break
                break;
            System.Threading.Thread.Sleep(1000); //no byte received re-check in another second;
        }

        if (byteCount <= 0) {
            //Log here accordingly. i.e.: clsError.LogError("TestSMS:GetMessage()", "No message received");
            return -3; //change this with your error code
        } //passing this point means you receive the message

        response = Encoding.UTF8.GetString(bytes);
        if ((response.ToString().IndexOf("ACK!1") == 0) || (response.ToString().IndexOf("ACK!MSGSTATUS=TRUE") == 0)) {
            clsError.LogError("TestSMS:SendReceiveTest()", "Message sent successfully:  " + response);
        }
        //log

    } catch (SocketException e) {
        clsError.LogError("TestSMS:SendReceiveTest()", e.Message.ToString());
        //return (e.ErrorCode); //not sure if this is a good idea, I rather change this.
        //If you really need e.ErrorCode, it would have been better to create `out` parameter in the method
        return -4; //Socket exception
    }
    return 0; // at this point, ideally there is no error at all
}
//-----------------End send------------------//
  1. Firstly, I will break up the nested if-else into

     if { /*simple case*/ return errorCode; } //continue 

    This way, it will be a lot easier to treat the error one-by-one as it occurs. (More will be shown later)

  2. I will introduce an error code to return different result, and then, if needed be, I would create switch handler on what to do upon different error codes. ie

     if (errorCode != 0){ //if there is error code. switch(errorCode){ case -1: //no ack //do something, such as repeating the process X times break; case -2: //fail to handshake //do something break; ... and so on } } 

    For this, I would also not returning the default socket error code (on exception ) by the function itself since it will interfere with my defined error code. But if needed be, this socket error code can be returned with out keyword in the method. Then, treat this socket error info under case -4: (using my example).

    Note: if there is any resend be needed because the acknowledgment is not received or for any other various error reasons, it should be done here.

     case -1: if (repeatNumber < repeatLimit) { SendReceiveTest(server, message); //repeats repeatNumber++; //reset this number when successful } else { //do something, it fails! } break; 
  3. Thirdly, as to the core of the issue, I would use server.Available as a means to check if there is any available data first before processing

     if (server.Available > 0) //check for the available byte first, please change 0 with number of bytes which you expected byteCount = server.Receive(bytes, server.Available, SocketFlags.None); //only read when byte is available 

    This way, it will not run the server.Receive command before the number of bytes receive the size that you expected as minimum.

    Note: there is still a possibility of split of packet (some data are received first, and some are received later). In such case, it would have been better to have a little more check before processing the data. The case I show you is limited to give the main idea on what to do on the main issue. There are online resources available to solve other issues (such as split of packet) which you could easily find.

  4. And lastly, come to the main portion of the trick, I would wrap the "listening" (or receive) part in a while loop for retrying purpose, with the help of retryCount , retryLimit , and System.Threading.Thread.Sleep .

     int retryCount = 0, retryLimit = 5; while (retryCount < retryLimit) { //repeats the waiting as long as it is within the retry limit if (server.Available > 0) //check for the available byte first, please change 0 with number of bytes which you expected byteCount = server.Receive(bytes, server.Available, SocketFlags.None); //only read when byte is available retryCount++; //Log the retry count here if your wish: clsError.LogError("Retry Count: " + retryCount.ToString(), "Retry attempt to get response"); if (byteCount > 0 && retryCount < retryLimit) //if there is byte received at this point or condition violated, break break; System.Threading.Thread.Sleep(1000); //no byte received re-check in another second; } if (byteCount <= 0) { // no byte received clsError.LogError("TestSMS:SendReceiveTest()", "No Acknowledgement recieved"); return -1; //change this with your own error code } //passing this point means you receive something 

All the best!

Just create a timer, stop it and dispose if you get your response before it fires. If you don't get your response before it elapses then it would fire another try. Also you'll probably need some counter to see how many timeouts there have been already and give up at some point so whole mechanism won't fall into endless try-and-fail loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM