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:
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. 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... 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------------------//
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)
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;
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.
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.