... | ... | @@ -415,4 +415,177 @@ public async Task<int> SendRecvCommandAsync(TcpClient client, string ipAddr, str |
|
|
// 생략
|
|
|
}
|
|
|
```
|
|
|
|
|
|
# 4. 예제 분석 2
|
|
|
EncClientManager는 UI프로그램과 장비간에 TCP 통신을 하는 모듈입니다.
|
|
|
|
|
|
UI와 장비간에는 통신 프로토콜이 정의되어 있구요, SendRecvCommandAsync() 메소드는 이를 구현한 메소드입니다.
|
|
|
|
|
|
시간되면 이 부분도 분석해 보면 좋겠습니다.
|
|
|
|
|
|
```csharp
|
|
|
// int를 반환하고 비동기로 수행될 메서드 선언
|
|
|
public async Task<int> SendRecvCommandAsync(TcpClient client, string ipAddr, string cmd, int ctag, int sessionID)
|
|
|
{
|
|
|
string myCmd = "%%" + cmd;
|
|
|
int calla_id = -1;
|
|
|
|
|
|
Trace.TraceInformation("[{4}] (THR:{5}) EncClientManager.SendRecvCommandAsync() begin : ipAddr={0}, ctag={1}, sess={2}, cmd={3}", ipAddr, ctag, sessionID, cmd, DateTime.Now, Thread.CurrentThread.ManagedThreadId);
|
|
|
|
|
|
try
|
|
|
{
|
|
|
// 비동기 호출로 해당 서버에 연결을 수행한다.
|
|
|
await client.ConnectAsync(ipAddr, EncClientManager.Port);
|
|
|
|
|
|
// 아래 코드는 비동기 호출이 완료되면 수행된다.
|
|
|
if (client.Connected)
|
|
|
{
|
|
|
using (var stream = client.GetStream())
|
|
|
{
|
|
|
///* 송신용 PDU 만들기 */
|
|
|
|
|
|
byte[] cmdBytes = Encoding.UTF8.GetBytes(myCmd);
|
|
|
int paddedCmdLen = cmdBytes.Length;
|
|
|
if ((paddedCmdLen % 2) == 1) /* 홀수 길이 */
|
|
|
paddedCmdLen++;
|
|
|
|
|
|
byte[] paddedCmdBytes = new byte[paddedCmdLen];
|
|
|
Array.Clear(paddedCmdBytes, 0, paddedCmdBytes.Length); /* == memset */
|
|
|
Array.Copy(cmdBytes, paddedCmdBytes, cmdBytes.Length);
|
|
|
|
|
|
byte[] sendBuffer = new byte[PDU_HDR_LEN + paddedCmdBytes.Length];
|
|
|
|
|
|
Array.Copy(PduSignature, sendBuffer, PduSignature.Length);
|
|
|
sendBuffer[(int)AppConst.PduHeader.version] = AppConst.PduDefaultVersion;
|
|
|
sendBuffer[(int)AppConst.PduHeader.type] = AppConst.PduRequestTL1;
|
|
|
|
|
|
ushort checksum = CRC.CheckSum16(paddedCmdBytes, paddedCmdBytes.Length);
|
|
|
byte[] checksumBytes = BitConverter.GetBytes(checksum);
|
|
|
Array.Reverse(checksumBytes);
|
|
|
Array.Copy(checksumBytes, 0, sendBuffer, (int)AppConst.PduHeader.checksun, checksumBytes.Length);
|
|
|
|
|
|
byte[] sessBytes = BitConverter.GetBytes(sessionID);
|
|
|
Array.Reverse(sessBytes);
|
|
|
Array.Copy(sessBytes, 0, sendBuffer, (int)AppConst.PduHeader.session, sessBytes.Length);
|
|
|
|
|
|
byte[] ctagBytes = BitConverter.GetBytes(ctag);
|
|
|
Array.Reverse(ctagBytes);
|
|
|
Array.Copy(ctagBytes, 0, sendBuffer, (int)AppConst.PduHeader.ctag, ctagBytes.Length);
|
|
|
|
|
|
byte[] lengthBytes = BitConverter.GetBytes(paddedCmdBytes.Length);
|
|
|
Array.Reverse(lengthBytes);
|
|
|
Array.Copy(lengthBytes, 0, sendBuffer, (int)AppConst.PduHeader.length, lengthBytes.Length);
|
|
|
|
|
|
Array.Copy(paddedCmdBytes, 0, sendBuffer, (int)AppConst.PduHeader.Size, paddedCmdBytes.Length);
|
|
|
|
|
|
// 연결된 TCP Client의 stream을 통해 명령어를 송신하고, 결과를 수신 받는다.
|
|
|
// WriteAsync, ReadAsyncs는 모두 비동기로 호출된다.
|
|
|
|
|
|
/* 명령어 송신 */
|
|
|
await stream.WriteAsync(sendBuffer, 0, sendBuffer.Length);
|
|
|
|
|
|
byte[] recvBuffer = new byte[AppConst.CallaBufferSize]; /* 수신 버퍼 */
|
|
|
int bytes; /* 수신 바이트수 */
|
|
|
|
|
|
/* 수신 PDU 까기 */
|
|
|
|
|
|
/* read HEADER */
|
|
|
bytes = await stream.ReadAsync(recvBuffer, 0, PDU_HDR_LEN);
|
|
|
if (bytes == 0)
|
|
|
{
|
|
|
Trace.TraceWarning("Command Execution FAIL. (HEADER Receive FAIL)");
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
/* check signature */
|
|
|
bool isValidSignature = true;
|
|
|
for (int i = 0; i < PduSignature.Length; i++)
|
|
|
{
|
|
|
if (recvBuffer[i] != PduSignature[i])
|
|
|
{
|
|
|
isValidSignature = false;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (!isValidSignature)
|
|
|
{
|
|
|
Trace.TraceWarning("Command Execution FAIL. (SIGNATURE ERROR)");
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
byte version;
|
|
|
byte type;
|
|
|
ushort recvChecksum;
|
|
|
int sessID;
|
|
|
int userTag;
|
|
|
int bodyLength;
|
|
|
|
|
|
version = recvBuffer[(int)AppConst.PduHeader.version];
|
|
|
type = recvBuffer[(int)AppConst.PduHeader.type];
|
|
|
|
|
|
if (type != AppConst.PduResponseCalla)
|
|
|
{
|
|
|
Trace.TraceWarning("**************************************");
|
|
|
Trace.TraceWarning("Command Execution FAIL. (TYPE MISMATCH : recv type = {0} != 1 (Calla Type))", type);
|
|
|
Trace.TraceWarning("**************************************");
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
Array.Reverse(recvBuffer, (int)AppConst.PduHeader.checksun, 2);
|
|
|
recvChecksum = BitConverter.ToUInt16(recvBuffer, (int)AppConst.PduHeader.checksun);
|
|
|
|
|
|
Array.Reverse(recvBuffer, (int)AppConst.PduHeader.session, 4);
|
|
|
sessID = BitConverter.ToInt32(recvBuffer, (int)AppConst.PduHeader.session);
|
|
|
|
|
|
Array.Reverse(recvBuffer, (int)AppConst.PduHeader.ctag, 4);
|
|
|
userTag = BitConverter.ToInt32(recvBuffer, (int)AppConst.PduHeader.ctag);
|
|
|
|
|
|
if (userTag != ctag)
|
|
|
{
|
|
|
Trace.TraceWarning("**************************************");
|
|
|
Trace.TraceWarning("[{2}] (THR:{3}) Command Execution FAIL. (CTAG MISMATCH : sent ctag = {0}, recv ctag = {1})", ctag, userTag, DateTime.Now, Thread.CurrentThread.ManagedThreadId);
|
|
|
Trace.TraceWarning("**************************************");
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
Array.Reverse(recvBuffer, (int)AppConst.PduHeader.length, 4);
|
|
|
bodyLength = BitConverter.ToInt32(recvBuffer, (int)AppConst.PduHeader.length);
|
|
|
|
|
|
/* read calla PDU */
|
|
|
int remainLength = bodyLength;
|
|
|
int destIndex = 0;
|
|
|
|
|
|
// PDU Header 정보를 이용해 calla 테이블 정보를 읽어들인다.
|
|
|
while (remainLength > 0)
|
|
|
{
|
|
|
int readAmount = await stream.ReadAsync(recvBuffer, destIndex, remainLength);
|
|
|
if (readAmount == 0)
|
|
|
{
|
|
|
Trace.TraceWarning("[{0}] (THR:{1}) Command Execution FAIL. (DATA RECEIVE FAIL)", DateTime.Now, Thread.CurrentThread.ManagedThreadId);
|
|
|
return -1;
|
|
|
}
|
|
|
remainLength -= readAmount;
|
|
|
destIndex += readAmount;
|
|
|
}
|
|
|
|
|
|
/* respons PDU --> calla PDU */
|
|
|
if (!Calla.SafeExtract(recvBuffer, bodyLength, out calla_id))
|
|
|
{
|
|
|
; // TODO.... PDU overflow or PDU crashed...
|
|
|
}
|
|
|
|
|
|
bool isValid = (Calla.valid(calla_id) == 1);
|
|
|
Trace.TraceInformation("[{5}] (THR:{6}) EncClientManager.SendRecvCommandAsync() complete : rsp calla({0}) isValid={1}, userTag={2}, seesID={3}, cmd={4}",
|
|
|
calla_id, isValid, userTag, sessID, cmd, DateTime.Now, Thread.CurrentThread.ManagedThreadId);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
catch (Exception /*ex*/)
|
|
|
{
|
|
|
//Trace.TraceInformation("[{0}] Exception : {1}", DateTime.Now, ex.Message);
|
|
|
}
|
|
|
|
|
|
return calla_id;
|
|
|
}
|
|
|
``` |
|
|
\ No newline at end of file |