When dealing with TCP Client, especially when the stream has to be encrypted, the message will not be passed unless the CryptoCtream is closed and this results in the inner stream to be not readable as it will be disposed.
For instance
TCPClient client = new TCPClient("some ip", 1234);
using(var i = new CryptoStream(client.GetStream(), myEncryptor(), CryptoStreamMode.Write) {
i.Write(some Data, 0, 1024);
}
I've tried the flush but it seems that the data can be communicated only FROM the client to server this way. Being aware of all the alternatives, I'm curious about how this could be made possible (sending data using TCP Client and receiving a response even without setting a second channel).
use an intermediate MemoryStream
var memstr = new MemoryStream();
using (var i = new CryptoStream(memstr.....)
{
i.Write(some data);
var buf = memstr.GetBuffer();
client.GetStream().Write(buf);
}
var inp = client.GetStream.Read(..);
var memstr2 = new MemoryStream(inp);
using (var o = new CryptoStream(memstr2,...))
{
var x = memstr2.Read();
}
Related
I was trying to accept some data from client, send it to the server and also send feedback from server to the client again. When server only gets some data, everething works fine. While trying to implement two-way communication, server just hungs.
Server's code:
var tcpListener = new TcpListener(IPAddress.Any, 8888);
try
{
tcpListener.Start();
Console.WriteLine("Server is currently run, waiting for incoming connections");
while (true)
{
using var tcpClient = await tcpListener.AcceptTcpClientAsync();
await using var stream = tcpClient.GetStream();
var incomeData = await DeserializeHelper.ConverteToBytes(stream);
var incomePerson = MessagePackSerializer.Deserialize<Person>(incomeData);
Console.WriteLine($"Income message from {incomePerson.Name}: '{incomePerson.Message}'");
var responseMessage = $"Thank you, {incomePerson.Name} for the message";
var responceData = MessagePackSerializer.Serialize(responseMessage);
await stream.WriteAsync(responceData);
}
}
finally
{
tcpListener.Stop();
}
Client's code:
using TcpClient tcpClient = new TcpClient();
await tcpClient.ConnectAsync("127.1.199.250",8888);
Console.WriteLine("Hello, client!");
try
{
var stream = tcpClient.GetStream();
Console.WriteLine("Enter your name");
var name = Console.ReadLine();
Console.WriteLine("Enter message");
var message = Console.ReadLine();
Person tom = new Person(name, message);
var sentData = MessagePackSerializer.Serialize(tom);
await stream.WriteAsync(sentData);
Console.WriteLine("Message was sent to the server");
var incomeResponce = await DeserializeHelper.ConverteToBytes(stream);
var incomeResponceMessage = MessagePackSerializer.Deserialize<string>(incomeResponce);
Console.WriteLine($"Server's message: {incomeResponce}");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Also all of them using my own static method ConverteToBytes, here it's code:
public static async Task<byte[]> ConverteToBytes(NetworkStream stream)
{
await using var ms = new MemoryStream();
byte[] bytesData = null;
int count = 0;
do
{
byte[] buffer = new byte[1024];
count = await stream.ReadAsync(buffer,0,1024);
ms.Write(buffer,0,count);
} while (stream.CanRead && count > 0);
return bytesData = ms.ToArray();
}
I guess the problem is with not correct stream using, but don't know what exactly is wrong. Thank's everyone!
I strongly recommend not using TCP/IP in the first place. If possible, use a well-established protocol with good library support, e.g., HTTP or WebSockets. Writing a correct TCP/IP application is extremely difficult.
If you must use TCP/IP, then I recommend watching my video series on the subject and reading my TCP/IP .NET Sockets FAQ.
Of particular note is that network streams are streams of bytes, not streams of messages. So there's no built-in way to know when a message completes. So you'll have to add that to your protocol yourself, i.e., using message framing. I describe message framing on my blog, and the video series linked above shows a more modern way to do it.
Without message framing, your server is likely just waiting for more bytes to arrive from the client connection.
I am connecting to a websocket server in C# using WebSocketClient class. I can connect to it successfully as the socket state changes to Open. When I try to receive data from the socket I get garbage data always.
the garbage data looks like following
\u0002\u0004\0\u0003Y\\0\04D\0\0\0\0^��$\0\0\0\0����
The client side code works fine if I connect to other server. Server is a third party.
var buffer = new ArraySegment<byte>(new byte[8192]);
using (var ms = new MemoryStream())
{
WebSocketReceiveResult result;
do
{
ct.ThrowIfCancellationRequested();
result = await socket.ReceiveAsync(buffer, ct);
ms.Write(buffer.Array), buffer.Offset, result.Count);
}
while (!result.EndOfMessage);
ms.Seek(0, SeekOrigin.Begin);
if (result.MessageType == WebSocketMessageType.Binary)
{
using (var reader = new StreamReader(ms, Encoding.UTF8))
{
//garbage data in str
var str = await reader.ReadToEndAsync();
return feed;
}
}
Any idea why this happens?
Third party server is http://antplus.aliceblueonline.com/#feeds-api
Data received through a socket using JsonSerializer:
var stream = new NetworkStream(socket);
var serializer = new JsonSerializer();
using (var sr = new StreamReader(stream, new UTF8Encoding(), false))
using (var jsonTextReader = new JsonTextReader(sr))
{
var result = serializer.Deserialize(jsonTextReader).ToString();
}
But the call to the Deserialize hangs when the length of data received from the socket is about 2890 bytes.
It can be easily reproduced by using this code (when jsonArrayElementsCount = 192 - ok, when 193 - it hangs):
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
namespace JsonNetHangsTest
{
class Program
{
static void Main(string[] args)
{
const int port = 11999;
const int jsonArrayElementsCount = 193;
var serverStartedEvent = new ManualResetEvent(false);
var clientReceivedEvent = new ManualResetEvent(false);
#region server
ThreadPool.QueueUserWorkItem(work =>
{
var server = new TcpListener(IPAddress.Parse("0.0.0.0"), port);
server.Start();
serverStartedEvent.Set();
var serverSocket = server.AcceptSocket();
var jsonString = "[\r\n" + String.Join(",", Enumerable.Repeat(" \"testdata\"\r\n", jsonArrayElementsCount)) + "]";
var bytes = new UTF8Encoding().GetBytes(jsonString);
serverSocket.Send(bytes);
Console.WriteLine("server send: " + bytes.Length);
clientReceivedEvent.WaitOne();
});
#endregion
serverStartedEvent.WaitOne();
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("127.0.0.1", port);
var stream = new NetworkStream(socket);
var serializer = new JsonSerializer();
using (var sr = new StreamReader(stream, new UTF8Encoding(), false))
using (var jsonTextReader = new JsonTextReader(sr))
{
var result = serializer.Deserialize(jsonTextReader).ToString();
Console.WriteLine("client receive: " + new UTF8Encoding().GetBytes(result).Length);
}
clientReceivedEvent.Set();
Console.ReadKey();
}
}
}
What am I missing here?
Your problem is that the tail end of the JSON you are sending is getting buffered on the server side. From the docs:
There is also no guarantee that the data you send will appear on the network immediately. To increase network efficiency, the underlying system may delay transmission until a significant amount of outgoing data is collected. A successful completion of the Send method means that the underlying system has had room to buffer your data for a network send.
Thus you need to ensure the socket buffer gets flushed. However, there's no Flush() method on Socket. So, what to do?
The easiest thing to do is to dispose the socket as soon as you are done with it:
int byteCount;
using (var serverSocket = server.AcceptSocket())
{
byteCount = serverSocket.Send(bytes);
}
Console.WriteLine("server send: " + byteCount);
(Disposing of disposables as soon as you are done with them is a good idea always.)
Alternatively, wrap the serverSocket in a NetworkStream that owns the socket, then dispose of the stream (which amounts to the same thing):
var serverSocket = server.AcceptSocket();
using (var ns = new NetworkStream(serverSocket, true))
{
ns.Write(bytes, 0, bytes.Length);
}
(But see NetworkStream.Write vs. Socket.Send which suggests it's sometimes necessary to check the value returned from serverSocket.Send(bytes).)
You might want to consider handling SocketError.NoBufferSpaceAvailable. Here's one suggestion for how to do it: C# Examples: Socket Send and Receive [C#].
I want to create for each request return response in socket c# and android.
I find the relevant socket and send a request, asking for the data I need.
After sending the request, I receive bytes until it sends the response. Then I stop receiving.
My server will need to handle many clients at once, and preferably multiple requests from a client at once. I need both the client and server to be able to receive messages at any time
I wrote this code:
message = Encoding.UTF8.GetString(dataRec,0,c);
dataRec = new byte[2048];
switch(message)
{
case "list-menu":
sendListMenu();
break;
case "login":
isLogin(message);
break;
}
login method
public void isLogin(string str){
string message = "";
Model_Users users;
dataSend=Encoding.UTF8.GetBytes("send-result");
newsocket.Send(dataSend);
//newsocket.close(); if close not receive
c = newsocket.Receive(dataRec);
message = Encoding.UTF8.GetString(dataRec,0,c);
XmlSerializer xml = new XmlSerializer(typeof(Model_Users));
using (TextReader reader = new StringReader(message))
{
if (reader != null)
{
users = (Model_Users)xml.Deserialize(reader);
MessageBox.Show(users.username);
dataSend = Encoding.UTF8.GetBytes("success-login");
newsocket.Send(dataSend);
newsocket.Close();
}
}
}
android code (client):
socket = new Socket();
socketAddress = new InetSocketAddress(cursor.getString(cursor.getColumnIndex("ip")), 9999);
socket.connect(socketAddress, 10000);
bufferWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferWriter.write(getrequest);
bufferWriter.flush();
String rvdMsgTxt = "";
stringBuilder = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while ((rvdMsgTxt = bufferedReader.readLine()).equals("success-login")) {
stringBuilder.append(rvdMsgTxt);
bufferedReader.mark(100);
bufferedReader.reset();
}
bufferWriter.write(XMLConvertor.usersSerializeXML("user", "pass"));
bufferWriter.flush();
But this doesn't work.
this SOLVED by newsocket.Shutdown(SocketShutdown.Send);
I have a weird problem trying to get full response from a web page using TcpClient which I send a POST to. Here the code:
byte[] RecvBufferxxxx = new byte[4096];
var returnData = "";
var uri = new Uri(string.Format(core.serverAuth, "app_data"));
var head = new WebHeaderCollection();
head[HttpRequestHeader.Host] = uri.Host;
head[HttpRequestHeader.Connection] = "keep-alive";
head[HttpRequestHeader.AcceptEncoding] = "deflate";
using (var client = new TcpClient(uri.Host, 443))
{
client.SendTimeout = 10000;
client.ReceiveTimeout = 10000;
using (SslStream s = new SslStream(client.GetStream(), false,
IgnoreCertificateErrorHandler, null))
{
s.AuthenticateAsClient(uri.Host, null, SslProtocols.Tls, false);
var hhd = "POST " + uri.PathAndQuery + " HTTP/1.0\r\n" + head;
var bts = Encoding.ASCII.GetBytes(hhd);
s.Write(bts, 0, bts.Length);
s.Flush();
s.ReadByte();
var n = s.Read(RecvBufferxxxx, 0, RecvBufferxxxx.Length);
// var tmp = Encoding.ASCII.GetString(RecvBufferxxxx, 0, n);
// ANOTHER CALL SAMPLE
// n = s.Read(RecvBufferxxxx, 0, RecvBufferxxxx.Length);
using (MemoryStream ms = new MemoryStream(RecvBufferxxxx, 0, n))
{
ms.ReadByte();
ms.ReadByte();
using (DeflateStream df = new DeflateStream(ms,
CompressionMode.Decompress))
{
using (StreamReader rd = new StreamReader(df))
{
returnData = rd.ReadToEnd();
}
}
}
}
}
This code works, but it gets only the response headers, I need to make another call to get the response body and i don't know why.
The response is from my server and is very short.
Before I was using only Socket and it was getting all in one call, but now i have rewritten it as in code adding SSL and deflate.
I have check the same link in firefox with firebug and there is only one get and full response.
I have double check it with wireshark and firebug, using firebug and this code wireshark listing looks quite the same.
I can make the second read with this code and then i get the response body, but then i see in wireshark that there was another ssl connection made and i don't want this, i want just as firefox does this.
Another reason is i just want to know why this is happing an dhow to fix these, can someone help me ?
Stream.Read() may not give you the whole buffer, it is allowed to return less. If you want to read the whole stream, you have to call it in a loop until it returns 0.
Ok, found the solution, but not the problem origin, just use ReadByte in loop, don't know why Read has problems, using reflector i was able to find that it could be a problem with SslStream internal buffering, but who knows.