... | @@ -129,7 +129,7 @@ static void threadFunc(object inst) |
... | @@ -129,7 +129,7 @@ static void threadFunc(object inst) |
|
```
|
|
```
|
|
|
|
|
|
### **Interlocked**
|
|
### **Interlocked**
|
|
**Interlocked**는 다중 Thread의 공유자원에 대한 **Atomic Operation**를 제공한다. 특히 64bit 변수의 연산 과정에서 동기화 문제가 생길 수 있는데 **Atomic Operation**을 하면 하나의 Thread에서 한 번에 처리할 수 있다. 이 경우에 **Monitor**을 사용하여 Thread를 동기화할 수 있지만, **Interlocked**에서 제공하는 연산 기능으로 간단하게 구현할 수 있다.
|
|
**Interlocked**는 다중 Thread의 공유자원에 대한 **Atomic Operation**을 제공한다. 특히 64bit 변수의 연산 과정에서 동기화 문제가 생길 수 있는데 **Atomic Operation**을 하면 하나의 Thread에서 한 번에 처리할 수 있다. 이 경우에 **Monitor**를 사용하여 Thread를 동기화할 수 있지만, **Interlocked**에서 제공하는 연산 기능으로 간단하게 구현할 수 있다.
|
|
|
|
|
|
```
|
|
```
|
|
static void threadFunc(object inst)
|
|
static void threadFunc(object inst)
|
... | @@ -144,7 +144,7 @@ static void threadFunc(object inst) |
... | @@ -144,7 +144,7 @@ static void threadFunc(object inst) |
|
```
|
|
```
|
|
|
|
|
|
### **ThreadPool**
|
|
### **ThreadPool**
|
|
**ThreadPool**은 필요할 때마다 Thread를 꺼내 쓰고 다시 반환할 수 있는 기능을 제공한다. **Thread**를 따로 생성할 필요 없이 `QueueUserWorkItem()`를 사용하면 Thread가 자동으로 생성되고 지정한 Method가 할당되어 실행한다.
|
|
**ThreadPool**은 필요할 때마다 Thread를 꺼내 쓰고 다시 반환할 수 있는 기능을 제공한다. **Thread**를 따로 생성할 필요 없이 `QueueUserWorkItem()`을 사용하면 Thread가 자동으로 생성되고 지정한 Method가 할당되어 실행한다.
|
|
|
|
|
|
```
|
|
```
|
|
static void Main()
|
|
static void Main()
|
... | @@ -336,26 +336,30 @@ Result : 5 |
... | @@ -336,26 +336,30 @@ Result : 5 |
|
## **예제 분석**
|
|
## **예제 분석**
|
|
### **SendRecvCommandTimeoutAsync()**
|
|
### **SendRecvCommandTimeoutAsync()**
|
|
```
|
|
```
|
|
public async Task<Tuple<bool, int>> SendRecvCommandTimeoutAsync(/* 생략 */) { /* 생략 */ }
|
|
public async Task<Tuple<bool, int>> SendRecvCommandTimeoutAsync(/* 매개변수 */) { /* 내용 */ }
|
|
```
|
|
```
|
|
* `acyns`를 사용했으므로 비동기 호출이 가능한 Method이다.
|
|
* `acyns`를 사용했으므로 비동기 호출이 가능한 Method이다.
|
|
* `bool`과 `int` 형식의 값을 저장한 `Tuple` 객체를 반환한다.
|
|
* `bool`과 `int` 형식의 값을 저장한 `Tuple` 객체를 가진 Task를 반환한다.
|
|
|
|
|
|
```
|
|
```
|
|
Task<int> cmdTask = SendRecvCommandAsync(client, ipAddr, cmd, ctag, sessionID);
|
|
Task<int> cmdTask = SendRecvCommandAsync(client, ipAddr, cmd, ctag, sessionID);
|
|
```
|
|
```
|
|
* ENC로 부터 PDU를 전송받아 **calla_id**로 변환된 값을 반환하는 Task인 **SendRecvCommandAsync()**를 **cmdTask**에 저장한다.
|
|
* ENC로 부터 PDU를 전송받아 calla_id로 변환된 값을 반환하는 Task인 SendRecvCommandAsync()를 cmdTask에 저장한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
```
|
|
```
|
|
Task timeoutTask = Task.Delay(timeoutMillis);
|
|
Task timeoutTask = Task.Delay(timeoutMillis);
|
|
```
|
|
```
|
|
* 현재 Method에서 매개변수로 받은 값인 **timeoutMillis**(기본 값:10000) 만큼 Delay하는 Task를 **timeoutTask**에 저장한다.
|
|
* 현재 Method에서 매개변수로 받은 값인 timeoutMillis(기본 값:10000) 만큼 Delay 하는 Task를 timeoutTask에 저장한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
```
|
|
```
|
|
result = await Task.Factory.ContinueWhenAny<bool>(new Task[] { cmdTask, timeoutTask }, (completedTask) => { /* 생략 */ });
|
|
result = await Task.Factory.ContinueWhenAny<bool>(new Task[] { cmdTask, timeoutTask }, (completedTask) => { /* 내용 */ });
|
|
```
|
|
```
|
|
* **cmdTask**와 **timeoutTask**를 동시에 실행하여 먼저 끝난 Task를 Func Delegate의 매개변수인 **completedTask**에 넘긴 후 Task를 실행한다. `await`를 사용했으므로 이 모든 Task는 비동기로 처리되고, Task의 반환 값은 **result**(`bool`)에 저장한다.
|
|
* cmdTask와 timeoutTask를 동시에 실행하여 먼저 끝난 Task를 Func Delegate의 매개변수인 completedTask에 넘긴 후 Task를 실행한다. `await`를 사용했으므로 이 모든 Task는 비동기로 처리되고, Task의 반환 값은 result에 저장한다.
|
|
* **Task.Factory.ContinueWhenAny()**와 관련된 내용과 예제를 설명 하단에 추가하였다.
|
|
* Task.Factory.ContinueWhenAny()와 관련된 내용과 예제를 설명 하단에 추가하였다.
|
|
|
|
|
|
```
|
|
```
|
|
if (completedTask == timeoutTask)
|
|
if (completedTask == timeoutTask)
|
... | @@ -371,7 +375,9 @@ else |
... | @@ -371,7 +375,9 @@ else |
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
```
|
|
```
|
|
* **completedTask**와 **timeoutTask**가 같다는 것은 cmdTask을 작업하는 데 제한시간을 초과했다는 것이므로 `false`를 반환한다. 이외 경우도 시간 초과로 보고 `false`를 반환한다.
|
|
* completedTask와 timeoutTask가 같다는 것은 cmdTask을 작업하는 데 제한시간을 초과했다는 것이므로 `false`를 반환한다. 이외 경우도 시간 초과로 보고 `false`를 반환한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
```
|
|
```
|
|
else if (completedTask == cmdTask)
|
|
else if (completedTask == cmdTask)
|
... | @@ -387,13 +393,15 @@ else if (completedTask == cmdTask) |
... | @@ -387,13 +393,15 @@ else if (completedTask == cmdTask) |
|
return (isValid == 1);
|
|
return (isValid == 1);
|
|
}
|
|
}
|
|
```
|
|
```
|
|
* **completedTask**와 **cmdTask**가 같다는 것은 제한시간 내에 **cmdTask**에서 **calla_id**를 반환했다는 것이다.
|
|
* completedTask와 cmdTask가 같다는 것은 제한시간 내에 cmdTask에서 calla_id를 반환했다는 것이다.
|
|
* **Calla.valid()**에서 **calla_id**를 확인한다. Invalid(-1)하면 Close하고, Valid(1)하면 `true`를 반환한다.
|
|
* calla_id가 유효한 ID인지를 확인한다. Invalid(-1)하면 Close하고, Valid(1)하면 `true`를 반환한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
```
|
|
```
|
|
return new Tuple<bool, int>(result, calla_id);
|
|
return new Tuple<bool, int>(result, calla_id);
|
|
```
|
|
```
|
|
* **Task.Factory.ContinueWhenAny()**에서 반환된 **result**와 **cmdTask**에서 반환된 **calla_id** 변수를 `Tuple`에 저장 후 객체를 생성하여 최종적으로 반환한다.
|
|
* Task.Factory.ContinueWhenAny()에서 반환된 result와 cmdTask에서 반환된 calla_id 변수를 `Tuple`에 저장 후 객체를 생성하여 최종적으로 반환한다.
|
|
|
|
|
|
#### **Task.Factory.ContinueWhenAny()**
|
|
#### **Task.Factory.ContinueWhenAny()**
|
|
예제를 살펴보면 첫 번째 매개변수의 Task 배열 중 먼저 끝난 Task가 두 번째 Func Delegate의 매개변수로 전달되는 것을 알 수 있다.
|
|
예제를 살펴보면 첫 번째 매개변수의 Task 배열 중 먼저 끝난 Task가 두 번째 Func Delegate의 매개변수로 전달되는 것을 알 수 있다.
|
... | @@ -444,3 +452,399 @@ public async void asyncTest() |
... | @@ -444,3 +452,399 @@ public async void asyncTest() |
|
```
|
|
```
|
|
|
|
|
|
### **SendRecvCommandAsync()**
|
|
### **SendRecvCommandAsync()**
|
|
|
|
```
|
|
|
|
// App.Const.cs
|
|
|
|
|
|
|
|
public static class AppConst
|
|
|
|
{
|
|
|
|
// PDU Header
|
|
|
|
public enum PduHeader : int
|
|
|
|
{
|
|
|
|
signature = 0, // [4] SIGNATURE $HI$"
|
|
|
|
version = 4, // [1] VERSION 1
|
|
|
|
type = 5, // [1] VERSION reqTL1(0), rspCalla(1), rspTL1(2)
|
|
|
|
checksun = 6, // [2] CHECKSUM sum(body)
|
|
|
|
session = 8, // [4] SESSION ID
|
|
|
|
ctag = 12, // [4] USER TAG
|
|
|
|
length = 16,// [4] BODY LENGTH
|
|
|
|
Size = 20
|
|
|
|
};
|
|
|
|
|
|
|
|
public const int PduDefaultVersion = 1;
|
|
|
|
public const int PduRequestTL1 = 0;
|
|
|
|
public const int PduResponseCalla = 1;
|
|
|
|
public const int PduSessionFreePass = 999999999;
|
|
|
|
public const int PduSessionInitial = 0;
|
|
|
|
|
|
|
|
// Calla Const
|
|
|
|
public const int CallaHeaderSize = 24;
|
|
|
|
public const int CallaOneRowSize = 1024;
|
|
|
|
|
|
|
|
public const int CallaBufferNumber = 50;
|
|
|
|
public const int CallaBufferSize = (150 * CallaOneRowSize) + CallaHeaderSize;
|
|
|
|
|
|
|
|
// TCP PORT
|
|
|
|
public const int EncTcpPort = 3300;
|
|
|
|
|
|
|
|
/* 생략 */
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
// CRC.CheckSum16() Method 수정
|
|
|
|
|
|
|
|
ushort CheckSum16(byte[] ptr, int len)
|
|
|
|
{
|
|
|
|
uint sum;
|
|
|
|
ushort chsum;
|
|
|
|
byte over;
|
|
|
|
|
|
|
|
byte index = 0; // 추가
|
|
|
|
|
|
|
|
sum = 0;
|
|
|
|
for (; len > 1; len -= 2)
|
|
|
|
{
|
|
|
|
sum += ptr[index];
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 1)
|
|
|
|
{
|
|
|
|
ptr[index] = (byte)(ptr[index] & 0xff00);
|
|
|
|
sum += (uint)(ptr[index] >> 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
over = (byte)(sum >> 16);
|
|
|
|
chsum = (ushort)(~(over + (ushort)sum));
|
|
|
|
|
|
|
|
return chsum;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
// SendRecvCommandAsync()에 필요한 임의의 변수들 선언
|
|
|
|
|
|
|
|
TcpClient client = new TcpClient();
|
|
|
|
string ipAddr = "192.168.0.100";
|
|
|
|
string cmd = "TEST";
|
|
|
|
int ctag = 1234;
|
|
|
|
int sessionID = AppConst.PduSessionFreePass; // 999999999
|
|
|
|
|
|
|
|
Task<int> cmdTask = SendRecvCommandAsync(client, ipAddr, cmd, ctag, sessionID);
|
|
|
|
```
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
public async Task<int> SendRecvCommandAsync(TcpClient client, string ipAddr, string cmd, int ctag, int sessionID) { /* 내용 */ }
|
|
|
|
```
|
|
|
|
* `acyns`를 사용했으므로 비동기 호출이 가능한 Method이다.
|
|
|
|
* `int` 형식의 값을 가진 Task를 반환한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
string myCmd = "%%" + cmd;
|
|
|
|
int calla_id = -1;
|
|
|
|
```
|
|
|
|
* 전송할 명령어 앞에 기본적으로 "%%"를 추가한다. (ex. %%TEST)
|
|
|
|
* calla_id를 -1로 초기화해준다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
await client.ConnectAsync(ipAddr, EncClientManager.Port);
|
|
|
|
```
|
|
|
|
* IP(ipAddr)와 Port(EncClientManager.Port) TCP Sever(ENC)에 Client를 연결한다.
|
|
|
|
* TCP Server와 연결하는 과정에서 대기시간이 길어지게 되면 병목이 발생하여 프로그램을 실행하는 동안에 문제가 발생할 수 있다. `await`를 사용해 비동기 처리를 함으로써 이러한 문제를 방지할 수 있다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
using (var stream = client.GetStream())
|
|
|
|
{
|
|
|
|
/* 내용 */
|
|
|
|
}
|
|
|
|
```
|
|
|
|
* 연결된 Client에서 데이터를 송수신하는 데 사용되는 Stream을 가져온다.
|
|
|
|
* `using` 키워드를 사용하여 코드가 끝나거나 예외가 발생했을 때 Dispose()를 호출하여 객체의 할당을 자동으로 해제한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
byte[] cmdBytes = Encoding.UTF8.GetBytes(myCmd);
|
|
|
|
int paddedCmdLen = cmdBytes.Length;
|
|
|
|
if ((paddedCmdLen % 2) == 1)
|
|
|
|
paddedCmdLen++;
|
|
|
|
```
|
|
|
|
* 전송할 명령어를 byte로 Encoding하여 cmdBytes에 저장한다.
|
|
|
|
* cmdBytes의 길이가 홀수이면 명령어의 크기를 1 증가시켜 짝수로 만들어 준다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
byte[] paddedCmdBytes = new byte[paddedCmdLen];
|
|
|
|
Array.Clear(paddedCmdBytes, 0, paddedCmdBytes.Length);
|
|
|
|
Array.Copy(cmdBytes, paddedCmdBytes, cmdBytes.Length);
|
|
|
|
```
|
|
|
|
* 명령어 크기만큼의 byte 배열(paddedCmdBytes)을 생성한다.
|
|
|
|
* paddedCmdBytes 배열의 모든 값을 0으로 초기화한다.
|
|
|
|
* cmdBytes 배열 전체를 paddedCmdBytes에 복사한다. paddedCmdBytes에는 Payload를 저장하고 있다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
byte[] sendBuffer = new byte[PDU_HDR_LEN + paddedCmdBytes.Length];
|
|
|
|
```
|
|
|
|
* PDU의 Header와 paddedCmdBytes의 길이를 더한 만큼의 byte 배열(sendBuffer)을 생성한다. sendBuffer는 ENC로 전송될 PDU가 될 것이다.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
Array.Copy(PduSignature, sendBuffer, PduSignature.Length);
|
|
|
|
```
|
|
|
|
* 정의된 PduSignature의 값을 sendBuffer의 첫 번째 요소부터 복사한다.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
sendBuffer[(int)AppConst.PduHeader.version] = AppConst.PduDefaultVersion;
|
|
|
|
```
|
|
|
|
* PduDefaultVersion를 sendBuffer의 4번 요소에 저장한다.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
sendBuffer[(int)AppConst.PduHeader.type] = AppConst.PduRequestTL1;
|
|
|
|
```
|
|
|
|
* PduRequestTL1를 sendBuffer의 5번 요소에 저장한다.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
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);
|
|
|
|
```
|
|
|
|
* paddedCmdBytes에 대한 CRC 값을 checksum을 저장한다.
|
|
|
|
* checksum을 byte로 Convert 해서 checksumBytes에 저장한다.
|
|
|
|
* checksumBytesd를 뒤집어서 sendBuffer의 6~7번 요소에 복사한다.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
byte[] sessBytes = BitConverter.GetBytes(sessionID);
|
|
|
|
Array.Reverse(sessBytes);
|
|
|
|
Array.Copy(sessBytes, 0, sendBuffer, (int)AppConst.PduHeader.session, sessBytes.Length);
|
|
|
|
```
|
|
|
|
* sessionID를 byte로 Convert 해서 sessBytes에 저장한다.
|
|
|
|
* sessBytes를 뒤집어서 sendBuffer의 8~11번 요소에 복사한다.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
byte[] ctagBytes = BitConverter.GetBytes(ctag);
|
|
|
|
Array.Reverse(ctagBytes);
|
|
|
|
Array.Copy(ctagBytes, 0, sendBuffer, (int)AppConst.PduHeader.ctag, ctagBytes.Length);
|
|
|
|
```
|
|
|
|
* ctag를 byte로 Convert 해서 ctagBytes에 저장한다.
|
|
|
|
* ctagBytes를 뒤집어서 sendBuffer의 12~15번 요소에 복사한다.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
byte[] lengthBytes = BitConverter.GetBytes(paddedCmdBytes.Length);
|
|
|
|
Array.Reverse(lengthBytes);
|
|
|
|
Array.Copy(lengthBytes, 0, sendBuffer, (int)AppConst.PduHeader.length, lengthBytes.Length);
|
|
|
|
```
|
|
|
|
* paddedCmdBytes의 길이를 byte로 Convert 해서 lengthBytes에 저장한다.
|
|
|
|
* lengthBytes를 뒤집어서 sendBuffer의 16~19번 요소에 복사한다.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
Array.Copy(paddedCmdBytes, 0, sendBuffer, (int)AppConst.PduHeader.Size, paddedCmdBytes.Length);
|
|
|
|
```
|
|
|
|
* paddedCmdBytes를 sendBuffer의 20번 요소부터 복사한다.
|
|
|
|
* ENC로 전송될 PDU의 정보가 sendBuffer에 모두 저장되었다.
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
await stream.WriteAsync(sendBuffer, 0, sendBuffer.Length);
|
|
|
|
```
|
|
|
|
* 현재 Stream에 해당하는 ENC로 PDU(sendBuffer)를 비동기 처리로 전송한다.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
byte[] recvBuffer = new byte[AppConst.CallaBufferSize];
|
|
|
|
```
|
|
|
|
* 전송받을 Calla PDU 크기만큼의 byte 배열(recvBuffer)을 생성한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
int bytes;
|
|
|
|
|
|
|
|
bytes = await stream.ReadAsync(recvBuffer, 0, PDU_HDR_LEN);
|
|
|
|
if (bytes == 0)
|
|
|
|
{
|
|
|
|
Trace.TraceWarning(/* 생략 */);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
* 현재 Stream에 해당하는 ENC로부터 Header 길이만큼의 byte 데이터를 비동기 처리로 전송받는다.
|
|
|
|
* 전송받은 byte 수가 0이면 오류로 판단하여 -1을 반환한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
bool isValidSignature = true;
|
|
|
|
for (int i = 0; i < PduSignature.Length; i++)
|
|
|
|
{
|
|
|
|
if (recvBuffer[i] != PduSignature[i])
|
|
|
|
{
|
|
|
|
isValidSignature = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isValidSignature)
|
|
|
|
{
|
|
|
|
Trace.TraceWarning(/* 생략 */);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
* 정의된 Signature와 recvBuffer의 Signature가 같은지 확인하고, 다르면 오류로 판단하여 -1을 반환한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
byte version;
|
|
|
|
byte type;
|
|
|
|
ushort recvChecksum;
|
|
|
|
int sessID;
|
|
|
|
int userTag;
|
|
|
|
int bodyLength;
|
|
|
|
```
|
|
|
|
* PDU의 Header 정보를 각각 저장할 변수를 선언한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
version = recvBuffer[(int)AppConst.PduHeader.version];
|
|
|
|
type = recvBuffer[(int)AppConst.PduHeader.type];
|
|
|
|
|
|
|
|
if (type != AppConst.PduResponseCalla)
|
|
|
|
{
|
|
|
|
Trace.TraceWarning(/* 생략 */);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
* recvBuffer의 4번 요소의 값을 version에 저장한다.
|
|
|
|
* recvBuffer의 5번 요소의 값을 type에 저장한다.
|
|
|
|
* type이 1(Calla Response)이 아니면 오류로 판단하여 -1을 반환한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
Array.Reverse(recvBuffer, (int)AppConst.PduHeader.checksun, 2);
|
|
|
|
recvChecksum = BitConverter.ToUInt16(recvBuffer, (int)AppConst.PduHeader.checksun);
|
|
|
|
```
|
|
|
|
* recvBuffer의 6~7번 요소의 값을 뒤집은 후 UInt16으로 Convert 해서 recvChecksum에 저장한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
Array.Reverse(recvBuffer, (int)AppConst.PduHeader.session, 4);
|
|
|
|
sessID = BitConverter.ToInt32(recvBuffer, (int)AppConst.PduHeader.session);
|
|
|
|
```
|
|
|
|
* recvBuffer의 8~11번 요소의 값을 뒤집은 후 Int32로 Convert 해서 sessID에 저장한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
Array.Reverse(recvBuffer, (int)AppConst.PduHeader.ctag, 4);
|
|
|
|
userTag = BitConverter.ToInt32(recvBuffer, (int)AppConst.PduHeader.ctag);
|
|
|
|
|
|
|
|
if (userTag != ctag)
|
|
|
|
{
|
|
|
|
Trace.TraceWarning(/* 생략 */);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
* recvBuffer의 12~15번 요소의 값을 뒤집은 후 Int32로 Convert 해서 userTag에 저장한다.
|
|
|
|
* ENC로 전송했던 User Tag(ctag)와 userTag가 다르면 오류로 판단하여 -1을 반환한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
Array.Reverse(recvBuffer, (int)AppConst.PduHeader.length, 4);
|
|
|
|
bodyLength = BitConverter.ToInt32(recvBuffer, (int)AppConst.PduHeader.length);
|
|
|
|
```
|
|
|
|
* recvBuffer의 16~19번 요소의 값을 뒤집은 후 Int32로 Convert 해서 bodyLength에 저장한다.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
```
|
|
|
|
int remainLength = bodyLength;
|
|
|
|
int destIndex = 0;
|
|
|
|
|
|
|
|
while (remainLength > 0)
|
|
|
|
{
|
|
|
|
int readAmount = await stream.ReadAsync(recvBuffer, destIndex, remainLength);
|
|
|
|
if (readAmount == 0)
|
|
|
|
{
|
|
|
|
Trace.TraceWarning(/* 생략 */);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
remainLength -= readAmount;
|
|
|
|
destIndex += readAmount;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
* 전송받은 Payload의 길이를 확인하기 위해 remainLength를 bodyLength의 값으로 저장하고, 전송받은 길이를 저장할 destIndex를 0으로 초기화 한다.
|
|
|
|
* 이 전에 ReadAsync()를 통해 Header 부분은 전송을 받았으므로, Body 부분부터 전송을 받을 것이다. 전송받은 위치부터 남은 길이만큼의 byte 데이터를 비동기 처리로 전송받는다.
|
|
|
|
* Payload의 길이가 남아있는 상태에서 전송받은 데이터가 없으면 오류로 판단하여 -1을 반환한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
if (!Calla.SafeExtract(recvBuffer, bodyLength, out calla_id))
|
|
|
|
{
|
|
|
|
; // TODO.... PDU overflow or PDU crashed...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
* 전송받은 Payload를 Parsing해서 calla_id에 저장한다.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
bool isValid = (Calla.valid(calla_id) == 1);
|
|
|
|
```
|
|
|
|
* calla_id가 유효한 ID인지를 확인한다. (Valid = 1, Invalid = -1)
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
```
|
|
|
|
return calla_id;
|
|
|
|
```
|
|
|
|
* SendRecvCommandAsync()는 최종적으로 calla_id를 반환한다. |
|
|
|
\ No newline at end of file |