성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
[공진영] 안녕하세요 좋은글 감사합니다. 현재 제가 wpf로 관제 모...
글쓰기
제목
이름
암호
전자우편
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# - Linux에서의 Named Pipe를 이용한 통신</h1> <p> Named Pipe는 윈도우뿐만 아니라,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 프로세스 간 통신 시 소켓 필요 없이 간단하게 Pipe를 열어 통신하는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1751'>http://www.sysnet.pe.kr/2/0/1751</a> .NET 코드 - Named Pipe 닷넷 서버와 VC++ 클라이언트 제작 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/971'>http://www.sysnet.pe.kr/2/0/971</a> C# - Hyper-V 가상 머신의 직렬 포트와 연결된 Named Pipe 간의 통신 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11722'>http://www.sysnet.pe.kr/2/0/11722</a> </pre> <br /> 리눅스 환경에서도 지원합니다. 물론, 구현 방식은 운영체제의 특성을 타기 때문에 그런 차이는 감안을 해야 합니다. 그래도 역시 닷넷 수준에서는 사용 방식이 윈도우에서와 유사합니다.<br /> <br /> <pre style='height: 460px; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 서버 측 using (<span style='color: blue; font-weight: bold'>NamedPipeServerStream pipe = new NamedPipeServerStream("pipe_test1", PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte))</span> { while (true) { Console.WriteLine("[Server] Waiting for client connection..."); <span style='color: blue; font-weight: bold'>pipe.WaitForConnection();</span> Console.WriteLine("[Server] Client Connected."); string txt = ReadString(pipe); Console.WriteLine("[Server] Read: " + txt); WriteString(pipe, "Echo: " + txt); <span style='color: blue; font-weight: bold'>pipe.Disconnect();</span> } } // 클라이언트 측 <span style='color: blue; font-weight: bold'>using (NamedPipeClientStream pipe = new NamedPipeClientStream("pipe_test1"))</span> { <span style='color: blue; font-weight: bold'>pipe.Connect(3000);</span> WriteString(pipe, "test2"); Console.WriteLine("[Client] Read: " + ReadString(pipe)); } static void WriteString(PipeStream pipe, string text) { byte [] buf = Encoding.UTF8.GetBytes(text); WriteInt32(pipe, buf.Length); pipe.Write(buf, 0, buf.Length); } static string ReadString(PipeStream pipe) { int sentBytes = ReadInt32(pipe); byte [] buf = new byte[sentBytes]; pipe.Read(buf, 0, buf.Length); return Encoding.UTF8.GetString(buf, 0, buf.Length); } static void WriteInt32(PipeStream pipe, int value) { byte [] buf = BitConverter.GetBytes(value); pipe.Write(buf, 0, buf.Length); } static int ReadInt32(PipeStream pipe) { byte [] intBytes = new byte[4]; pipe.Read(intBytes, 0, 4); return BitConverter.ToInt32(intBytes, 0); } </pre> <br /> 웹 검색을 해보면 리눅스의 mkfifo가,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Linux mkfifo Command Tutorial for Beginners (with Examples) ; <a target='tab' href='https://www.howtoforge.com/linux-mkfifo-command/'>https://www.howtoforge.com/linux-mkfifo-command/</a> the default access permissions for named pipes is 'rw', 'rw', and 'r' (for user, group, and others, respectively). </pre> <br /> NamedPipeServerStream의 기반은 아니고 소켓 기반이라고 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > NamedPipeServerStream creates a socket not a fifo on ubuntu ; <a target='tab' href='https://github.com/dotnet/corefx/issues/25792'>https://github.com/dotnet/corefx/issues/25792</a> </pre> <br /> 실제로 Named Pipe를 생성하면 "p" 유형이 아닌 "s" 유형으로 생성하는 데, 예를 들어 위와 같은 코드로 pipe를 생성하면 다음과 같은 식의 파일이 생성됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > $ ls /tmp/Core* -l srwxrwxr-x. 1 tuser tuser 0 Jun 26 13:48 /tmp/CoreFxPipe_tuser.F.RJrq9HeuOr <span style='color: blue; font-weight: bold'>srwxrwxr-x. 1 tuser tuser 0 Jun 26 14:17 /tmp/CoreFxPipe_pipe_test1</span> srwxrwxr-x. 1 tuser tuser 0 Jun 26 14:17 /tmp/CoreFxPipe_vsdbg-ui-95cbe06919614425931f87c78e612760 </pre> <br /> (<a target='tab' href='https://github.com/stjeong/DotNetSamples/tree/master/NetCore/NamedPipeSample'>이 글의 예제 코드는 github - NamedPipeSample</a>에서 제공합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> <a target='tab' href='http://www.sysnet.pe.kr/2/0/11963'>Unix Domain Socket</a>과 마찬가지로 파일 기반이라는 점 때문에 root 권한의 프로세스에서 pipe를 생성한 것과, 일반 사용자 권한으로 pipe를 생성한 것은 각각 접근 권한이 다릅니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [root 권한으로 실행] srwxr-xr-x. 1 root root 0 Jun 26 11:44 /tmp/CoreFxPipe_pipe_test1 [user 권한으로 실행] srwxrwxr-x. 1 tuser tuser 0 Jun 26 14:17 /tmp/CoreFxPipe_pipe_test1 </pre> <br /> 따라서 root 프로세스가 생성한 pipe를 일반 사용자 프로세스에서 연결하려면 다음과 같은 식의 예외가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Permission denied /tmp/CoreFxPipe_pipe_test1 Exception has occurred: CLR/System.Net.Internals.SocketExceptionFactory.ExtendedSocketException An unhandled exception of type 'System.Net.Internals.SocketExceptionFactory.ExtendedSocketException' occurred in System.IO.Pipes.dll: 'Permission denied' at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress) at System.Net.Sockets.Socket.Connect(EndPoint remoteEP) at System.IO.Pipes.NamedPipeClientStream.TryConnect(Int32 timeout, CancellationToken cancellationToken) at System.IO.Pipes.NamedPipeClientStream.ConnectInternal(Int32 timeout, CancellationToken cancellationToken, Int32 startTime) at System.IO.Pipes.NamedPipeClientStream.Connect(Int32 timeout) at testd.Program.Main(String[] args) in /home/tuser/testd/Program.cs:line 34 </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;' > $ sudo chmod 777 /tmp/CoreFxPipe_pipe_test1 $ ls /tmp/CoreFxPipe_pipe_test1 -l srwxrwxrwx. 1 root root 0 Jun 26 16:14 /tmp/CoreFxPipe_pipe_test1 </pre> <br /> 문제는 pipe 이름 앞에 "/tmp/CoreFxPipe_"가 붙는다는 것인데 딱히 공식 문서가 없는 걸로 봐서 향후에도 100% 변경되지 않는다는 보장은 할 수 없습니다. (<a target='tab' href='https://github.com/dotnet/corefx/blob/40cbd5636851093309b841db4d53af369b7ef1c6/src/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs#L29'>소스 코드에 하드 코딩된 형태</a>입니다.)<br /> <br /> 또는, root가 생성한 pipe 서버가 read 권한은 허용하므로 client가 연결하면서 rw 유형의 pipe 서버를 새로 생성해 서버로 하여금 그 pipe에 연결하도록 (번거롭지만) 구현하는 것도 가능할 것입니다.<br /> <br /> 어쨌든 그렇다 해도, /tmp 폴더에 생성됨으로 인해 systemd-tmpfiles 또는 tmpwatch 등의 데몬으로부터 자유로울 수 없는데, 사용(Read/Write) 동작에도 파일의 Access/Modify/Change 시간이 변경되지 않기 때문입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> root 권한의 프로세스가 생성했어도 특별히 자식 프로세스에 한해 별다른 권한 조정 없이 통신할 수 있는 방법도 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > AnonymousPipeServerStream ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.io.pipes.anonymouspipeserverstream?view=netcore-2.1'>https://learn.microsoft.com/en-us/dotnet/api/system.io.pipes.anonymouspipeserverstream?view=netcore-2.1</a> </pre> <br /> 익명이기 때문에 /tmp 폴더에 파일이 생성되지 않으므로 systemd-tmpfiles 또는 tmpwatch 등의 데몬으로부터 안전하다는 장점도 있습니다.<br /> <br /> 여러 가능성을 종합해보면, Named Pipe 자원은 가능한 부모/자식 프로세스 사이에서의 IPC 통신으로 사용하는 것이 좋습니다. 그 외의 경우라면 Domain Socket을 고려하거나, 아니면 포트 관리의 귀찮음만 아니면 일반 소켓도 "127.0.0.1"로 바인딩해 사용하는 것도 좋습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> WaitForPipeDrain 메서드의 경우 리눅스에서는 지원하지 않으므로 다음의 예외가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Unhandled Exception: System.PlatformNotSupportedException: Operation is not supported on this platform. at System.IO.Pipes.PipeStream.WaitForPipeDrain() at testd.ServiceModule.threadFunc(Object obj) in D:\testd\ServiceModule.cs:line 58 at System.Threading.Thread.ThreadMain_ParameterizedThreadStart(Object parameter) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() </pre> <br /> <hr style='width: 50%' /><br /> <br /> 현재 시점(2019-06-29)의 NamedPipeServerStream은 리눅스에서 PipeTransmissionMode.Message 유형을 지원하지 않습니다. 이를 사용하려고 하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using (NamedPipeServerStream pipe = new NamedPipeServerStream("pipe_test1", PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message)) { } </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;' > Unhandled Exception: <span style='color: blue; font-weight: bold'>System.PlatformNotSupportedException: Message transmission mode is not supported on this platform.</span> at System.IO.Pipes.NamedPipeServerStream.Create(String pipeName, PipeDirection direction, Int32 maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, Int32 inBufferSize, Int32 outBufferSize, HandleInheritability inheritability) at System.IO.Pipes.NamedPipeServerStream..ctor(String pipeName, PipeDirection direction, Int32 maxNumberOfServerInstances, PipeTransmissionMode transmissionMode, PipeOptions options, Int32 inBufferSize, Int32 outBufferSize, HandleInheritability inheritability) at System.IO.Pipes.NamedPipeServerStream..ctor(String pipeName, PipeDirection direction, Int32 maxNumberOfServerInstances, PipeTransmissionMode transmissionMode) at testd.Program.threadFunc() in /home/tuser/testd/Program.cs:line 37 at System.Threading.Thread.ThreadMain_ThreadStart() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1072
(왼쪽의 숫자를 입력해야 합니다.)