성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
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'>WCF에서 DataSet을 binary encoding으로 직렬화하는 방법</h1> <p> 테스트용으로 다음과 같이 DataSet을 반환하는 WCF를 만들고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public DataSet GetDataSet() { DataSet ds = new DataSet(); DataColumn nameCol = new DataColumn("Name", typeof(string)); DataColumn birthCol = new DataColumn("Birth", typeof(DateTime)); DataColumn emailCol = new DataColumn("Email", typeof(string)); DataColumn familyCol = new DataColumn("Family", typeof(byte)); DataTable table = new DataTable("MemberInfo"); table.Columns.Add(nameCol); table.Columns.Add(birthCol); table.Columns.Add(emailCol); table.Columns.Add(familyCol); table.Rows.Add("Anderson", new DateTime(1950, 5, 20), "anderson@gmail.com", 2); table.Rows.Add("Jason", new DateTime(1967, 12, 3), "jason@gmail.com", 0); table.Rows.Add("Mark", new DateTime(1998, 3, 2), "mark@naver.com", 1); table.Rows.Add("Jennifer", new DateTime(1985, 5, 6), "jennifer@jennifersoft.com", 0); ds.Tables.Add(table); return ds; } </pre> <br /> WCF 클라이언트로서 윈폼 앱을 만들어 다음과 같이 호출했습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var ch = new ChannelFactory<ITestService>( "customHttpBinding_ITestService", new EndpointAddress("http://169.254.223.95:2524/TestService.svc")); var svc = ch.CreateChannel(); DataSet ds = svc.GetDataSet(); System.Diagnostics.Trace.WriteLine(ds.GetXml()); </pre> <br /> <a target='tab' href='http://www.sysnet.pe.kr/2/0/1487'>이 사이의 통신을 가로채면</a> 다음과 같습니다. (Body 영역은 보기 편하게 일부러 들여쓰기 편집을 했습니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > POST http://169.254.223.95:2524/TestService.svc HTTP/1.1 Content-Type: text/xml; charset=utf-8 VsDebuggerCausalityData: uIDPo+slpM8ZksBGuDd+qgAC8QYAAAAAj4yuJGaVKESSxzkDQNjf3pIuoAs6l5FJlArYEHPUGKcACQAA SOAPAction: "http://tempuri.org/ITestService/GetDataSet" Host: 169.254.223.95:2524 <span style='color: blue; font-weight: bold'>Content-Length: 135</span> Expect: 100-continue Accept-Encoding: gzip, deflate <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <GetDataSet xmlns="http://tempuri.org/"/> </s:Body> </s:Envelope> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HTTP/1.1 200 OK Content-Type: text/xml; charset=utf-8 Vary: Accept-Encoding Server: Microsoft-IIS/8.0 X-SourceFiles: =?UTF-8?B?ZDpcc2V0dGluZ3NcRGVza3RvcFxkYXRhc2V0X2FzX2JpblxXaW5kb3dzRm9ybXNBcHBsaWNhdGlvbjFcV2ViQXBwbGljYXRpb24xXFRlc3RTZXJ2aWNlLnN2Yw==?= X-Powered-By: ASP.NET Date: Tue, 27 Aug 2013 05:53:36 GMT <span style='color: blue; font-weight: bold'>Content-Length: 1884</span> <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body>< GetDataSetResponse xmlns="http://tempuri.org/"> <GetDataSetResult> <xs:schema id="NewDataSet" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="MemberInfo"> <xs:complexType> <xs:sequence> <xs:element name="Name" type="xs:string" minOccurs="0"/> <xs:element name="Birth" type="xs:dateTime" minOccurs="0"/> <xs:element name="Email" type="xs:string" minOccurs="0"/> <xs:element name="Family" type="xs:unsignedByte" minOccurs="0"/> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema> <diffgr:diffgram xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <NewDataSet xmlns=""> <MemberInfo diffgr:id="MemberInfo1" msdata:rowOrder="0" diffgr:hasChanges="inserted"><Name>Anderson</Name><Birth>1950-05-20T00:00:00+09:00</Birth><Email>anderson@gmail.com</Email><Family>2</Family></MemberInfo><MemberInfo diffgr:id="MemberInfo2" msdata:rowOrder="1" diffgr:hasChanges="inserted"><Name>Jason</Name><Birth>1967-12-03T00:00:00+09:00</Birth><Email>jason@gmail.com</Email><Family>0</Family></MemberInfo><MemberInfo diffgr:id="MemberInfo3" msdata:rowOrder="2" diffgr:hasChanges="inserted"><Name>Mark</Name><Birth>1998-03-02T00:00:00+09:00</Birth><Email>mark@naver.com</Email><Family>1</Family></MemberInfo><MemberInfo diffgr:id="MemberInfo4" msdata:rowOrder="3" diffgr:hasChanges="inserted"><Name>Jennifer</Name><Birth>1985-05-06T00:00:00+09:00</Birth><Email>jennifer@jennifersoft.com</Email><Family>0</Family> </MemberInfo> </NewDataSet> </diffgr:diffgram> </GetDataSetResult> </GetDataSetResponse> </s:Body> </s:Envelope> </pre> <br /> "Content-Length: 1884"라는 결과가 나오는군요. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 사실, 이 테스트를 해보는 것은 예전의 고객사 사이트에서 발생한 GZIP 인코딩 문제 때문입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 제니퍼 닷넷 적용 사례 (4) - GZIP 인코딩으로 인한 성능 하락 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1484'>http://www.sysnet.pe.kr/2/0/1484</a> System.IO.MemoryStream, ArraySegment<T> 의 효율적인 사용법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1483'>http://www.sysnet.pe.kr/2/0/1483</a> </pre> <br /> GZIP을 풀어놓자니 DataSet 크기가 여전히 마음에 걸립니다. 그나마 성능 손실 없이 데이터를 조금이라도 줄이는 방법은 바이너리 인코딩을 쓰는 것이 대안일 수 있는데요. 의외로 방법은 간단합니다. 우선, 서버 쪽 web.config에서 기존의 바인딩을 다음과 같이 binaryMessageEncoding + httpTransport로 적용해 주고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <?xml version="1.0"?> <configuration> <!-- ...[생략]... --> <system.serviceModel> <bindings> <customBinding> <binding name="HttpBinaryBindingConfig"> <span style='color: blue; font-weight: bold'> <binaryMessageEncoding> <readerQuotas maxStringContentLength="65536000" maxArrayLength="65536000" maxBytesPerRead="4096000" /> </binaryMessageEncoding> <httpTransport maxReceivedMessageSize="65536000" transferMode="Buffered" hostNameComparisonMode="StrongWildcard" authenticationScheme="Anonymous" /></span> </binding> </customBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="TestService_Behavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="TestService_Behavior" name="WebApplication1.TestService"> <endpoint address="" binding="customBinding" bindingConfiguration="HttpBinaryBindingConfig" contract="WebApplication1.ITestService"> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> <serviceHostingEnvironment multipleSiteBindingsEnabled="true" /> </system.serviceModel> </configuration> </pre> <br /> 클라이언트 측의 app.config도 그에 맞게 변경시켜주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <customBinding> <binding name="customHttpBinding_ITestService"> <span style='color: blue; font-weight: bold'> <binaryMessageEncoding> <readerQuotas maxDepth="32" maxStringContentLength="65536000" maxArrayLength="65536000" maxBytesPerRead="4096000" maxNameTableCharCount="16384" /> </binaryMessageEncoding> <httpTransport maxReceivedMessageSize="65536000" transferMode="Buffered" hostNameComparisonMode="StrongWildcard" authenticationScheme="Anonymous" /></span> </binding> </customBinding> </bindings> <client> <endpoint address="http://169.254.223.95:2524/TestService.svc" binding="customBinding" bindingConfiguration="customHttpBinding_ITestService" contract="ServiceReference1.ITestService" name="customHttpBinding_ITestService" /> </client> </system.serviceModel> </configuration> </pre> <br /> 그 외 변경되는 것은 아무것도 없습니다. 이렇게만 바꿔주고 통신을 시작하면 다음과 같이 클라이언트 <-> 서버 간에 통신이 바이너리로 이뤄지는 것을 확인할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > POST http://169.254.223.95:2524/TestService.svc HTTP/1.1 Content-Type: application/soap+msbin1 Host: 169.254.223.95:2524 Content-Length: 329 Expect: 100-continue Accept-Encoding: gzip, deflate Connection: Close V s aVD ???*http://tempuri.org/ITestService/GetDataSetD???i??p?L???jY?D,D*?@VsDebuggerCausalityDataAhttp://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink?<???_??*s?I??:?L{???????$f?(D??9@???.? :??I? ?s??? ??D???*http://169.254.223.95:2524/TestService.svcV@ GetDataSethttp://tempuri.org/ </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HTTP/1.1 200 OK <span style='color: blue; font-weight: bold'>Content-Length: 1512</span> Content-Type: application/soap+msbin1 Server: Microsoft-IIS/8.0 X-SourceFiles: =?UTF-8?B?ZDpcc2V0dGluZ3NcRGVza3RvcFxkYXRhc2V0X2FzX2JpblxXaW5kb3dzRm9ybXNBcHBsaWNhdGlvbjFcV2ViQXBwbGljYXRpb24xXFRlc3RTZXJ2aWNlLnN2Yw==?= X-Powered-By: ASP.NET Date: Tue, 27 Aug 2013 08:08:23 GMT Connection: close V s aVD ???2http://tempuri.org/ITestService/GetDataSetResponseD???i??p?L???jY?V@GetDataSetResponsehttp://tempuri.org/@GetDataSetResultAxsschemaid? NewDataSet xs http://www.w3.org/2001/XMLSchema? msdata$urn:schemas-microsoft-com:xml-msdataAxselementname? NewDataSetmsdata IsDataSet?truemsdataUseCurrentLocale?trueAxs complexTypeAxschoice minOccurs? maxOccurs? unboundedAxselementname? MemberInfoAxs complexTypeAxssequenceAxselementname?Nametype? xs:string minOccurs?Axselementname?Birthtype? xs:dateTime minOccurs?Axselementname?Emailtype? xs:string minOccurs?Axselementname?Familytype?xs:unsignedByte minOccurs?Adiffgrdiffgram diffgr)urn:schemas-microsoft-com:xml-diffgram-v1 msdata$urn:schemas-microsoft-com:xml-msdata@ NewDataSet?@ MemberInfodiffgrid? MemberInfo1msdatarowOrder?diffgr hasChanges?inserted@Name?Anderson@Birth?1950-05-20T00:00:00+09:00@Email?anderson@gmail.com@Family?2@ MemberInfodiffgrid? MemberInfo2msdatarowOrder?diffgr hasChanges?inserted@Name?Jason@Birth?1967-12-03T00:00:00+09:00@Email?jason@gmail.com@Family?@ MemberInfodiffgrid? MemberInfo3msdatarowOrder?2diffgr hasChanges?inserted@Name?Mark@Birth?1998-03-02T00:00:00+09:00@Email?mark@naver.com@Family?@ MemberInfodiffgrid? MemberInfo4msdatarowOrder?3diffgr hasChanges?inserted@Name?Jennifer@Birth?1985-05-06T00:00:00+09:00@Email?jennifer@jennifersoft.com@Family? </pre> <br /> 이전 textMessageEncoding + HTTP로 했었을 때는 1884바이트였던 것이 binaryMessageEncoding + HTTP로 바꾸니 1512바이트로 약 20% 정도의 데이터 길이가 감소했습니다. 거의 공짜로 감소된 것 치고는 괜찮은 비율입니다. 그런데, 좀 만족스럽지 않군요. ^^<br /> <br /> 직렬화된 데이터를 보니 diffgram을 유지하고 있는 것이 별로 마음에 들지 않습니다. WCF 메소드에서 딱 1라인만 추가를 해봤습니다. (사실, 서비스로부터 받아오는 데이터의 diffgram이 유지될 이유가 별로 없지요. ^^)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public DataSet GetDataSet() { DataSet ds = new DataSet(); // ...[생략]... ds.Tables.Add(table); <span style='color: blue; font-weight: bold'>ds.AcceptChanges();</span> return ds; } </pre> <br /> 그런데, AcceptChanges로 인한 결과가 매우 흥미롭습니다. 이렇게 바뀌어진 WCF 메소드는 textMessageEncoding + HTTP로는 오히려 1998바이트로 늘어났고, binaryMessageEncoding + HTTP일 때는 1396바이트로 더 줄었습니다. 그럼 1884바이트 대비 1396바이트로 하면 36% 정도의 데이터가 감소했습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이에 관해서 혹시 누군가 비교한 데이터가 있지 않을까 찾아봤는데 다음의 글이 나왔습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > WCF over HTTPS, Compression, and Binary Binding ; <a target='tab' href='http://architects.dzone.com/articles/wcf-over-https-compression-and'>http://architects.dzone.com/articles/wcf-over-https-compression-and</a> </pre> <br /> <img alt='dataset_as_bin_1.png' src='/SysWebRes/bbs/dataset_as_bin_1.png' /><br /> <br /> 텍스트 인코딩인 경우 262,149 바이트였던 것을 바이너리로 바꾸면 155,565 바이트로 40%까지 감소되었다는 테스트 결과입니다. 물론, binaryMessageEncoding + GZIP 인코딩은 86%까지 감소되었다고 나오니 어느 것을 희생할 지는... 충분한 테스트를 거쳐서 판단하는 것이 좋겠습니다.<br /> <br /> 아니면 공사가 좀 크겠지만, DataSet보다 좀 더 경량화된 DTO(Data Transfer Object)의 사용이 답일 수도 있습니다.<br /> <br /> 풀리지 않는 숙제죠. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > DataSet 사용을 반대하는 의견 .NET Architecture Case Study #1 - Part 1 ; <a target='tab' href='http://blog.naver.com/saltynut/120168527432'>http://blog.naver.com/saltynut/120168527432</a> DataSet 사용을 찬성하는 의견 DataSet 이야기 ; <a target='tab' href='http://www.simpleisbest.net/post/2012/09/12/About-DataSet.aspx'>http://www.simpleisbest.net/post/2012/09/12/About-DataSet.aspx</a> </pre> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=789&boardid=331301885'>첨부된 파일은 위의 테스트를 진행한 프로젝트</a>입니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8535
(왼쪽의 숫자를 입력해야 합니다.)