各位大哥大姊好:
想請問用程式輪詢卡機,用送指令跟接收卡機資訊,但有時候卡機訊息會不完全(黏包?)的問題
已經用執行敘暫停 但還是會有問題,還是接收訊息的緩衝區要改小呢
謝謝
Byte[] sendmessage = new byte[] { 0x7e, 0x04, 0x01, 0x25, 0xdb, 0x01 }; //send message
Byte[] delsendmessage = new byte[] { 0x7e, 0x04, 0x01, 0x37, 0xc9, 0x01 }; //del指令
Byte[] opensendmessage = new byte[] { 0x7e, 0x05, 0x01, 0x21, 0x84, 0x5b, 0x01 }; //open door 指令 7E 05 01 21 84 5B 01
Byte[] showlcdmessage= new byte[] { 0x7e, 0x11, 0x01, 0x27, 0x20, 0x08, 0x77, 0x65, 0x6c, 0x63, 0x6f, 0x6d, 0x65, 0x21, 0x01, 0xf4, 0x03, 0x5c, 0xb1}; //show lcd
while (true) //一直迴圈執行
{
Thread tt1 = new Thread(new ParameterizedThreadStart(cb.SendMsg));
tt1.Start(sendmessage); //boxing
Console.WriteLine("TXS接收指令:" + staticClass.ToHexString(sendmessage, sendmessage.Length)); //靜態類別不用new
string[] array = cb.ReceiveMsg();
Console.WriteLine("RX:" +array[0]);//卡機回覆的資訊,如果沒回覆會卡住
Console.WriteLine("RX驗證碼1:" + array[2]);
Console.WriteLine("RX decision:" +array[1]);
if (array[1] == "Y") //ReceiveMsg 接收到的指令 長度31 and 第4個byte為0B
{
cb.SendMsg(showlcdmessage);
cb.SendMsg(opensendmessage);
cb.SendMsg(delsendmessage);
}
Thread.Sleep(100); //Delay 1秒
}
public void SendMsg(object data) 送指令
{
byte[] msg = (byte[]) data;
NetworkStream ns = tmpTcpClient.GetStream();
//得到從客戶端傳來的網路流
if (ns.CanWrite) //如果資料流支援寫入
{
ns.Write(msg, 0, msg.Length);
Thread.Sleep(100);
}
}
public string[] ReceiveMsg() //封包接收TcpClient tmpTcpClient
{
NetworkStream ns = tmpTcpClient.GetStream();
byte[] receiveBytes = new byte[tmpTcpClient.ReceiveBufferSize];
int numberOfBytesRead = 0;
int length = 0;
string receiveMsg = string.Empty;
string validbyte = string.Empty;
string cardinfo = string.Empty;
string decision = "O";
if (ns.CanRead) //裡面是區塊變數
{
do
{
numberOfBytesRead = ns.Read(receiveBytes, 0, tmpTcpClient.ReceiveBufferSize); //從 NetworkStream 讀取的位元組數 length
/*字串的轉換省略*/
if (!ns.DataAvailable) Thread.Sleep(100);
}
while (ns.DataAvailable);
}
return new string[] { receiveMsg, decision, validbyte };
}
在 ReceiveMsg 方法裡加入了緩衝區的處理,以防止黏包(TCP Sticky Packets)的問題。
public class CardReader
{
private TcpClient tcpClient;
public CardReader(string ipAddress, int port)
{
tcpClient = new TcpClient(ipAddress, port);
}
public void SendCommand(byte[] command)
{
NetworkStream stream = tcpClient.GetStream();
if (stream.CanWrite)
{
stream.Write(command, 0, command.Length);
}
}
public string[] ReceiveResponse()
{
NetworkStream stream = tcpClient.GetStream();
byte[] buffer = new byte[tcpClient.ReceiveBufferSize];
string response = string.Empty;
string decision = "O";
string validByte = string.Empty;
int bytesRead = 0;
int totalBytesRead = 0;
do
{
bytesRead = stream.Read(buffer, totalBytesRead, tcpClient.ReceiveBufferSize - totalBytesRead);
totalBytesRead += bytesRead;
}
while (stream.DataAvailable);
if (totalBytesRead > 0)
{
byte[] responseBytes = new byte[totalBytesRead];
Array.Copy(buffer, responseBytes, totalBytesRead);
response = BitConverter.ToString(responseBytes).Replace("-", " ");
decision = responseBytes[4] == 0x0B ? "Y" : "N";
validByte = responseBytes[5].ToString("X2");
}
return new string[] { response, decision, validByte };
}
public void Close()
{
tcpClient.Close();
}
}
你可以使用以上修改後的程式碼來取代原本的 SendMsg 和 ReceiveMsg 方法。在迴圈裡面的部分也可以改成如下的寫法:
while (true)
{
cb.SendCommand(sendmessage);
Console.WriteLine("TXS接收指令:" + BitConverter.ToString(sendmessage).Replace("-", " "));
string[] response = cb.ReceiveResponse();
Console.WriteLine("RX:" + response[0]);
Console.WriteLine("RX驗證碼1:" + response[2]);
Console.WriteLine("RX decision:" + response[1]);
if (response[1] == "Y")
{
cb.SendCommand(showlcdmessage);
cb.SendCommand(opensendmessage);
cb.SendCommand(delsendmessage);
}
Thread.Sleep(1000);
}