1 // System.Runtime.Remoting.Channels.Tcp.TcpMessageIO.cs
3 // Author: Lluis Sanchez Gual (lluis@ideary.com)
5 // (C) 2002 Lluis Sanchez Gual
8 // Permission is hereby granted, free of charge, to any person obtaining
9 // a copy of this software and associated documentation files (the
10 // "Software"), to deal in the Software without restriction, including
11 // without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to
13 // permit persons to whom the Software is furnished to do so, subject to
14 // the following conditions:
16 // The above copyright notice and this permission notice shall be
17 // included in all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System
.Runtime
.Serialization
;
30 using System
.Runtime
.Serialization
.Formatters
.Binary
;
31 using System
.Collections
;
34 using System
.Net
.Sockets
;
36 namespace System
.Runtime
.Remoting
.Channels
.Tcp
38 enum MessageStatus { MethodMessage = 0, CancelSignal = 1, Unknown = 10}
40 internal class TcpMessageIO
42 static byte[][] _msgHeaders
=
44 new byte[] { (byte)'.', (byte)'N', (byte)'E', (byte)'T', 1, 0 }
,
45 new byte[] { 255, 255, 255, 255, 255, 255 }
48 public static int DefaultStreamBufferSize
= 1000;
50 // Identifies an incoming message
51 public static MessageStatus
ReceiveMessageStatus (Stream networkStream
, byte[] buffer
)
54 StreamRead (networkStream
, buffer
, 6);
55 } catch (Exception ex
) {
56 throw new RemotingException ("Tcp transport error.", ex
);
61 bool[] isOnTrack
= new bool[_msgHeaders
.Length
];
62 bool atLeastOneOnTrack
= true;
65 while (atLeastOneOnTrack
)
67 atLeastOneOnTrack
= false;
69 for (int n
= 0; n
<_msgHeaders
.Length
; n
++)
71 if (i
> 0 && !isOnTrack
[n
]) continue;
73 isOnTrack
[n
] = (c
== _msgHeaders
[n
][i
]);
74 if (isOnTrack
[n
] && (i
== _msgHeaders
[n
].Length
-1)) return (MessageStatus
) n
;
75 atLeastOneOnTrack
= atLeastOneOnTrack
|| isOnTrack
[n
];
79 return MessageStatus
.Unknown
;
81 catch (Exception ex
) {
82 throw new RemotingException ("Tcp transport error.", ex
);
86 static bool StreamRead (Stream networkStream
, byte[] buffer
, int count
)
90 int pr
= networkStream
.Read (buffer
, nr
, count
- nr
);
92 throw new RemotingException ("Connection closed");
98 public static void SendMessageStream (Stream networkStream
, Stream data
, ITransportHeaders requestHeaders
, byte[] buffer
)
100 SendMessageStream (networkStream
, data
, requestHeaders
, buffer
, false);
103 public static void SendMessageStream (Stream networkStream
, Stream data
, ITransportHeaders requestHeaders
, byte[] buffer
, bool isOneWay
)
105 if (buffer
== null) buffer
= new byte[DefaultStreamBufferSize
];
107 // Writes the message start header
108 byte[] dotnetHeader
= _msgHeaders
[(int) MessageStatus
.MethodMessage
];
109 networkStream
.Write(dotnetHeader
, 0, dotnetHeader
.Length
);
111 // Writes the header tag
112 // 0x0000 - request stream
113 // 0x0001 - OneWay request stream
114 // 0x0002 - response stream
115 if(requestHeaders
[CommonTransportKeys
.RequestUri
]!=null) {
116 buffer
[0] = isOneWay
? (byte) 1 : (byte) 0;
119 buffer
[0] = (byte) 2;
121 buffer
[1] = (byte) 0 ;
124 buffer
[2] = (byte) 0;
126 // Writes assemblyID????
127 buffer
[3] = (byte) 0;
129 // Writes the length of the stream being sent (not including the headers)
130 int num
= (int)data
.Length
;
131 buffer
[4] = (byte) num
;
132 buffer
[5] = (byte) (num
>> 8);
133 buffer
[6] = (byte) (num
>> 16);
134 buffer
[7] = (byte) (num
>> 24);
135 networkStream
.Write(buffer
, 0, 8);
137 // Writes the message headers
138 SendHeaders (networkStream
, requestHeaders
, buffer
);
140 if (data
.Length
== 0)
144 if (data
is MemoryStream
)
146 // The copy of the stream can be optimized. The internal
147 // buffer of MemoryStream can be used.
148 MemoryStream memStream
= (MemoryStream
)data
;
149 networkStream
.Write (memStream
.GetBuffer(), 0, (int)memStream
.Length
);
153 int nread
= data
.Read (buffer
, 0, buffer
.Length
);
156 networkStream
.Write (buffer
, 0, nread
);
157 nread
= data
.Read (buffer
, 0, buffer
.Length
);
162 static byte[] msgUriTransportKey
= new byte[] { 4, 0, 1, 1 }
;
163 static byte[] msgContentTypeTransportKey
= new byte[] { 6, 0, 1, 1 }
;
164 static byte[] msgDefaultTransportKey
= new byte[] { 1, 0, 1 }
;
165 static byte[] msgHeaderTerminator
= new byte[] { 0, 0 }
;
167 private static void SendHeaders(Stream networkStream
, ITransportHeaders requestHeaders
, byte[] buffer
)
169 // Writes the headers as a sequence of strings
170 if (networkStream
!= null)
172 IEnumerator e
= requestHeaders
.GetEnumerator();
175 DictionaryEntry hdr
= (DictionaryEntry
)e
.Current
;
176 switch (hdr
.Key
.ToString())
178 case CommonTransportKeys
.RequestUri
:
179 networkStream
.Write (msgUriTransportKey
, 0, 4);
182 networkStream
.Write (msgContentTypeTransportKey
, 0, 4);
185 networkStream
.Write (msgDefaultTransportKey
, 0, 3);
186 SendString (networkStream
, hdr
.Key
.ToString(), buffer
);
187 networkStream
.WriteByte (1);
190 SendString (networkStream
, hdr
.Value
.ToString(), buffer
);
193 networkStream
.Write (msgHeaderTerminator
, 0, 2); // End of headers
196 public static ITransportHeaders
ReceiveHeaders (Stream networkStream
, byte[] buffer
)
198 StreamRead (networkStream
, buffer
, 2);
200 byte headerType
= buffer
[0];
201 TransportHeaders headers
= new TransportHeaders ();
203 while (headerType
!= 0)
206 StreamRead (networkStream
, buffer
, 1); // byte 1
209 case 4: key
= CommonTransportKeys
.RequestUri
; break;
210 case 6: key
= "Content-Type"; break;
211 case 1: key
= ReceiveString (networkStream
, buffer
); break;
212 default: throw new NotSupportedException ("Unknown header code: " + headerType
);
214 StreamRead (networkStream
, buffer
, 1); // byte 1
215 headers
[key
] = ReceiveString (networkStream
, buffer
);
217 StreamRead (networkStream
, buffer
, 2);
218 headerType
= buffer
[0];
224 public static Stream
ReceiveMessageStream (Stream networkStream
, out ITransportHeaders headers
, byte[] buffer
)
228 if (buffer
== null) buffer
= new byte[DefaultStreamBufferSize
];
230 // Reads header tag: 0 -> Stream with headers or 2 -> Response Stream
232 // Gets the length of the data stream
233 StreamRead (networkStream
, buffer
, 8);
235 int byteCount
= (buffer
[4] | (buffer
[5] << 8) |
236 (buffer
[6] << 16) | (buffer
[7] << 24));
239 headers
= ReceiveHeaders (networkStream
, buffer
);
242 byte[] resultBuffer
= new byte[byteCount
];
243 StreamRead (networkStream
, resultBuffer
, byteCount
);
244 return new MemoryStream (resultBuffer
);
246 return new MemoryStream ();
250 private static void SendString (Stream networkStream
, string str
, byte[] buffer
)
252 // Allocates a buffer. Use the internal buffer if it is
253 // big enough. If not, create a new one.
255 int maxBytes
= Encoding
.UTF8
.GetMaxByteCount(str
.Length
)+4; //+4 bytes for storing the string length
256 if (maxBytes
> buffer
.Length
)
257 buffer
= new byte[maxBytes
];
259 int num
= Encoding
.UTF8
.GetBytes (str
, 0, str
.Length
, buffer
, 4);
261 // store number of bytes (not number of chars!)
263 buffer
[0] = (byte) num
;
264 buffer
[1] = (byte) (num
>> 8);
265 buffer
[2] = (byte) (num
>> 16);
266 buffer
[3] = (byte) (num
>> 24);
268 // Write the string bytes
269 networkStream
.Write (buffer
, 0, num
+ 4);
272 private static string ReceiveString (Stream networkStream
, byte[] buffer
)
274 StreamRead (networkStream
, buffer
, 4);
276 // Reads the number of bytes (not chars!)
278 int byteCount
= (buffer
[0] | (buffer
[1] << 8) |
279 (buffer
[2] << 16) | (buffer
[3] << 24));
281 if (byteCount
== 0) return string.Empty
;
283 // Allocates a buffer of the correct size. Use the
284 // internal buffer if it is big enough
286 if (byteCount
> buffer
.Length
)
287 buffer
= new byte[byteCount
];
291 StreamRead (networkStream
, buffer
, byteCount
);
292 char[] chars
= Encoding
.UTF8
.GetChars (buffer
, 0, byteCount
);
294 return new string (chars
);