성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - CoAP 서버 및 클라이언트 제작 (UDP 소켓 통신)</h1> <p> HTTP 프로토콜에서 TLS 및 모바일 망의 효율을 위해 UDP를 적용하게 된 것처럼,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > .NET 5에서의 네트워크 라이브러리 개선 (2) - HTTP/2, HTTP/3 관련 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12504#http3'>https://www.sysnet.pe.kr/2/0/12504#http3</a> </pre> <br /> IoT 세계의 저전력을 목표로 하는 기기에서 TCP는 부담스러울 수밖에 없습니다. ^^ 그래서 나온 것이, UDP에 기반을 둔 <a target='tab' href='https://tools.ietf.org/html/rfc7252'>CoAP(The Constrained Application Protocol)</a>입니다.<br /> <br /> 비록, IoT 기기를 위한 프로토콜이긴 하지만 사실 그냥 일반적인 UDP 통신 래퍼로 여겨도 좋을 수준인데요, 그런 의미에서 ^^ C# 콘솔 프로그램으로 간단하게 CoAP 서버 및 클라이언트를 만들어 보겠습니다.<br /> <br /> 당연하겠지만, 이미 NuGet에 패키지가 있습니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > smeshlink/CoAP.NET ; <a target='tab' href='https://github.com/smeshlink/CoAP.NET'>https://github.com/smeshlink/CoAP.NET</a> CoAP.NET ; <a target='tab' href='https://www.nuget.org/packages/CoAP/'>https://www.nuget.org/packages/CoAP/</a> // Install-Package CoAP -Version 1.1.0 CoAP.NET.Core ; <a target='tab' href='https://www.nuget.org/packages/CoAP.NET.Core/'>https://www.nuget.org/packages/CoAP.NET.Core/</a> // Install-Package CoAP.NET.Core -Version 1.1.0 </pre> <br /> 일단, 서버는 CoAP.NET의 README.md에서 설명한 대로 다음과 같이 간단하게 제작할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using CoAP.Server; using System; using System.Diagnostics; class HelloWorldResource : Resource { public HelloWorldResource() : <span style='color: blue; font-weight: bold'>base("hello-world")</span> { Attributes.Title = "GET a friendly greeting!"; } protected override void <span style='color: blue; font-weight: bold'>DoGet</span>(CoapExchange exchange) { <span style='color: blue; font-weight: bold'>Console.WriteLine($"temperature data: {exchange.Request.PayloadString}");</span> exchange.Respond("Hello World from CoAP.NET!"); } } // Install-Package CoAP -Version 1.1.0 class Server { static void Main(String[] args) { CoAP.CoapConfig cc = new CoAP.CoapConfig(); cc.MaxRetransmit = 2; CoapServer server = new CoapServer(cc, <span style='color: blue; font-weight: bold'>60100</span>); <span style='color: blue; font-weight: bold'>server.Add(new HelloWorldResource());</span> server.Start(); Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } } /* 실행 결과 DEBUG - Starting CoAP server DEBUG - BlockwiseLayer uses MaxMessageSize: 1024 and DefaultBlockSize:512 DEBUG - Starting endpoint bound to [::]:60100 Press any key to exit... */ </pre> <br /> HelloWorldResource 타입의 base 생성자에 "hello-world"를 전달하고 있는데 ASP.NET Web API라면 일종의 "[Route("hello-world")]" 설정이라고 보면 됩니다.<br /> <br /> 어찌 보면, IoC 컨테이너 없는 단순한 수준의 Web API 템플릿이라고 봐도 무방할 정도입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이렇게 만든 CoAP 서버를 소비하는 클라이언트 역시 간단하게 만들 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // CoAP.Client - A CoAP Example Client // <a target='tab' href='https://github.com/smeshlink/CoAP.NET/tree/master/CoAP.Example/CoAP.Client'>https://github.com/smeshlink/CoAP.NET/tree/master/CoAP.Example/CoAP.Client</a> using CoAP; using System; using System.Diagnostics; // Install-Package CoAP -Version 1.1.0 class Program { static void Main(string[] args) { Request request = new Request(Method.GET); request.URI = new Uri(<span style='color: blue; font-weight: bold'>"coap://127.0.0.1:60100/hello-world"</span>); <span style='color: blue; font-weight: bold'>request.SetPayload("0", MediaType.TextPlain);</span> // 간단한 데이터 전송 <span style='color: blue; font-weight: bold'>request.Send();</span> Response response = <span style='color: blue; font-weight: bold'>request.WaitForResponse();</span> if (response == null) { Console.WriteLine("Failed to get response"); } } } /* 출력 결과: 서버 temperature data: 0 */ /* 출력 결과: 클라이언트 DEBUG - BlockwiseLayer uses MaxMessageSize: 1024 and DefaultBlockSize:512 DEBUG - Starting endpoint bound to [::]:61379 DEBUG - Scheduling retransmission for CON-GET ID=-1, Token=21773E8A, Options=[URI-Port=60100, URI-Path=hello-world, Content-Type=text/plain], "0" DEBUG - Send request, failed transmissions: 0 DEBUG - Stored open request by KeyID[59156 for ], KeyToken[21-77-3E-8A] DEBUG - Exchange got response: Cleaning up KeyID[59156 for ] DEBUG - Cancel retransmission for --> DEBUG - CON-GET ID=59156, Token=21773E8A, Options=[URI-Port=60100, URI-Path=hello-world, Content-Type=text/plain], "0" DEBUG - Exchange completed: Cleaning up KeyToken[21-77-3E-8A] */ </pre> <br /> 보는 바와 같이 UDP 통신이지만, Request/Response 기반으로 동작해 마치 HTTP REST API 호출하듯이 사용할 수 있습니다. 사용법이 너무 간단하죠. ^^ 결국, 일반 UDP 통신을 구현하는 몇몇 상황에서도 CoAP를 이용하는 것이 더 좋은 선택이 될 수 있을 정도입니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1764&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 경우에 따라 IoT 기기에서 데이터를 서버가 받아줄 때까지 무한정 재시도를 하기는 힘들 것입니다. 이를 위해 Send 전에 MaxRetransmit을 설정하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ...[생략]... // 재현을 위해 일부러 없는 포트로 전송 request.URI = new Uri("coap://127.0.0.1:<span style='color: blue; font-weight: bold'>13290</span>/hello-world"); <span style='color: blue; font-weight: bold'>request.MaxRetransmit = 2;</span> request.Send(); </pre> <br /> 다음과 같이 0, 1, 2까지 총 3번의 요청을 전송합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > DEBUG - BlockwiseLayer uses MaxMessageSize: 1024 and DefaultBlockSize:512 DEBUG - Starting endpoint bound to [::]:49943 DEBUG - Scheduling retransmission for CON-GET ID=-1, Token=026840C6, Options=[URI-Port=190, URI-Path=hello-world, Content-Type=text/plain], "0" <span style='color: blue; font-weight: bold'>DEBUG - Send request, failed transmissions: 0</span> DEBUG - Stored open request by KeyID[53977 for ], KeyToken[02-68-40-C6] DEBUG - Timeout: retransmit message, failed: 1, message: CON-GET ID=53977, Token=026840C6, Options=[URI-Port=190, URI-Path=hello-world, Content-Type=text/plain], "0" DEBUG - Scheduling retransmission for CON-GET ID=53977, Token=026840C6, Options=[URI-Port=190, URI-Path=hello-world, Content-Type=text/plain], "0" <span style='color: blue; font-weight: bold'>DEBUG - Send request, failed transmissions: 1</span> DEBUG - Stored open request by KeyID[53977 for ], KeyToken[02-68-40-C6] DEBUG - Timeout: retransmit message, failed: 2, message: CON-GET ID=53977, Token=026840C6, Options=[URI-Port=190, URI-Path=hello-world, Content-Type=text/plain], "0" DEBUG - Scheduling retransmission for CON-GET ID=53977, Token=026840C6, Options=[URI-Port=190, URI-Path=hello-world, Content-Type=text/plain], "0" <span style='color: blue; font-weight: bold'>DEBUG - Send request, failed transmissions: 2</span> DEBUG - Stored open request by KeyID[53977 for ], KeyToken[02-68-40-C6] DEBUG - Start Mark-And-Sweep with 0 entries <span style='color: blue; font-weight: bold'>DEBUG - Timeout: retransmission limit reached, exchange failed</span>, message: CON-GET ID=53977, Token=026840C6, Options=[URI-Port=190, URI-Path=hello-world, Content-Type=text/plain], "0" DEBUG - Exchange completed: Cleaning up KeyToken[02-68-40-C6] DEBUG - Exchange completed: Cleaning up KeyToken[02-68-40-C6] DEBUG - Exchange completed: Cleaning up KeyToken[02-68-40-C6] Press any key to continue . . . </pre> <br /> 혹은 Send 후 Response를 받는 메서드에서 timeout을 설정하는 것도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Request request = new Request(Method.GET); request.URI = new Uri("coap://127.0.0.1:190/hello-world"); request.SetPayload("0", MediaType.TextPlain); request.MaxRetransmit = 2; request.Send(); Response response = request.WaitForResponse(<span style='color: blue; font-weight: bold'>1000</span>); </pre> <br /> 그럼, MaxRetransmit 설정 횟수에 상관없이 무조건 1000ms 지난 후에 WaitForResponse는 null을 반환하게 됩니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
6274
(왼쪽의 숫자를 입력해야 합니다.)