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#].
Related
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();
}
Disclaimer: this is my first foray into anything directly tcp/socket related. I've read -the- -following- -resources- and am trying to come up with a very simple test application.
I'm trying to develop a local server running with a TcpListener object. I can instantiate it fine and run netstat to see the port in the LISTENING state. However, I can't telnet or create a test client to connect manually. Telnet says simply that it could not open a connection. Trying a test client application throws the exception
An unhandled exception of type 'System.Net.Sockets.SocketException' occurred in System.dll
Additional information: Only one usage of each socket address (protocol/network address/port) is normally permitted
In this SO question, the asker ultimately resolved it via a faulty NIC. How might I look for that?
This leaves me puzzled. How do I ever test or connect to the server? Here's my code.
For the server:
using System;
using System.Configuration;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace TcpConsole
{
class Program
{
static void Main(string[] args)
{
var localPort = ConfigurationManager.AppSettings["localPort"];
var portNumber = int.Parse(localPort);
var maxConnections = ConfigurationManager.AppSettings["maxConnections"];
var maxConnectionsNumber = int.Parse(maxConnections);
Console.WriteLine("Preparing to start server on port {0}", portNumber);
Console.WriteLine("Max connections: {0}", maxConnectionsNumber);
var ipHostInfo = Dns.GetHostEntry("localhost");
var ipAddress = ipHostInfo.AddressList[0];
var localEndPoint = new IPEndPoint(ipAddress, portNumber);
Console.WriteLine("Starting server with local IP {0}", ipAddress);
var listener = new TcpListener(localEndPoint);
listener.Start(maxConnectionsNumber);
Console.WriteLine();
Console.WriteLine("Server started...");
Console.WriteLine();
while (true)
{
var socket = listener.AcceptSocket();
Task.Factory.StartNew(() =>
{
ProcessSocket(socket);
});
}
}
private static async void ProcessSocket(Socket socket)
{
try
{
using (var stream = new NetworkStream(socket))
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
var received = await reader.ReadToEndAsync();
Console.WriteLine("Received: " + received);
}
socket.Close();
socket.Dispose();
}
catch (Exception exception)
{
Console.WriteLine("There was an error processing a message.");
Console.WriteLine(exception);
}
}
}
}
Seeing the above code running:
For the test application:
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
namespace TcpConsoleClient
{
class Program
{
static void Main(string[] args)
{
var ipHostInfo = Dns.GetHostEntry("localhost");
var ipAddress = ipHostInfo.AddressList[0];
var remoteEndPoint = new IPEndPoint(ipAddress, 3245);
var client = new TcpClient(remoteEndPoint);
using (var stream = client.GetStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
string input;
while((input = Console.ReadLine()) != "exit")
{
writer.WriteLine(input);
}
}
}
}
}
A TCP/IP connection has a local IP address and port, as well as a remote IP address and port.
Generally, a port number can only be assigned to one socket at a time. The error message indicates that an attempt was made to associate more than one socket with the same port number.
The reason why can be found in the client code:
var client = new TcpClient(remoteEndPoint);
This overload of the TcpClient constructor accepts an IPEndPoint specifying the local endpoint. That is, when the TcpClient is constructed, it will get bound to the port number specified by remoteEndPoint, which fails, because that port number is already in use by the server application.
To fix this, use the parameter-less TcpClient constructor, and instead pass remoteEndPoint to the Connect call:
var client = new TcpClient();
client.Connect(remoteEndPoint);
Here is a sample server and client code in c#. The server will send an array of string and the client will receive it and display and then the client will send an id and the server will receive it and display. But I am getting an exception in my the server while running both of them.
The exception is as follows:
An unhandled exception of type 'System.ObjectDisposedException' occurred in System.dll
Additional information: Cannot access a disposed object.
Client:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Xml.Serialization;
namespace Client
{
class Program
{
static void Main(string[] args)
{
try
{
byte[] data = new byte[1024];
string stringData;
TcpClient tcpClient = new TcpClient("127.0.0.1", 1234);
NetworkStream ns = tcpClient.GetStream();
var serializer = new XmlSerializer(typeof(string[]));
var stringArr = (string[])serializer.Deserialize(tcpClient.GetStream());
foreach (string s in stringArr)
{
Console.WriteLine(s);
}
string input = Console.ReadLine();
ns.Write(Encoding.ASCII.GetBytes(input), 0, input.Length);
ns.Flush();
}
catch (Exception e)
{
Console.Write(e.Message);
}
Console.Read();
}
}
}
Server:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Xml.Serialization;
namespace server
{
class Program
{
static void Main(string[] args)
{
TcpListener tcpListener = new TcpListener(IPAddress.Any, 1234);
tcpListener.Start();
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
byte[] data = new byte[1024];
NetworkStream ns = tcpClient.GetStream();
string[] arr1 = new string[] { "one", "two", "three" };
var serializer = new XmlSerializer(typeof(string[]));
serializer.Serialize(tcpClient.GetStream(), arr1);
tcpClient.Close();
int recv = ns.Read(data, 0, data.Length); //getting exception in this line
string id = Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(id);
}
}
}
}
Is there anything wrong?
What will I need to change to avoid this exception?
Once you call
tcpClient.Close();
it cleans up resources associated with it, including disposing ns.
The following line
int recv = ns.Read(data, 0, data.Length); //getting exception in this line
attempts to read from ns after you just (indirectly) disposed of it.
Do not close the connection until you are done with it. Also, use the using keyword instead of explicitly closing the connection, as it will ensure proper cleanup even if an exception is thrown.
ther is a server and client sockets. I want to learn a way how can I send a List<T>, IEnumerable<T> object to client to server or server to client.
i want to send server-side TaskManager Threads List to client-side. this code send line by line, I want to send complate list. How can I do it?
private static IEnumerable<string> TaskManager()
{
List<string> lst = new List<string>();
foreach (System.Diagnostics.Process p in
Process.GetProcesses().OrderBy(o => o.ProcessName))
{
lst.Add(p.ProcessName + "_" + p.Id);
}
return lst.AsParallel();
}
....
....
....
while (true)
{
Socket socket = Listener.AcceptSocket();
try
{
//open stream
Stream stream = new NetworkStream(socket);
StreamReader sr = new StreamReader(stream);
StreamWriter sw = new StreamWriter(stream);
sw.AutoFlush = true;
while (true)
{
Parallel.ForEach(
TaskManager(), item=>
sw.WriteLine(item)
);
}
stream.Close();
stream.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Disconnected: {0}", socket.RemoteEndPoint);
socket.Close();
socket.Dispose();
}
You can use BinaryFormatter to serialize and deserialze a List.
using System.Runtime.Serialization.Formatters.Binary;
// Server side
Stream stream = new NetworkStream(socket);
var bin = new BinaryFormatter();
bin.Serialize(stream, TaskManager());
// Client side
Stream stream = new NetworkStream(socket);
var bin = new BinaryFormatter();
var list = (List<string>)bin.Deserialize(stream);
You could implement all aspects of a solution yourself, i.e. serialisation, compression, sending, receiving etc. Most things can be found by reading about WCF or the System.Net namespace. I can however give you a very concise solution using the network library NetworkComms.Net here.
For the client:
using System;
using NetworkCommsDotNet;
namespace Client
{
class Program
{
static void Main(string[] args)
{
//Create a connection
Connection connection = TCPConnection.GetConnection(new ConnectionInfo("127.0.0.1", 10000));
//Make a request for the threadIds and get the answer in one statement.
string[] taskManagerThreadIds = connection.SendReceiveObject<string[]>("ThreadIdRequest", "ThreadIds", 2000);
Console.WriteLine("Server provided an array containing {0} ids", taskManagerThreadIds.Length);
Console.WriteLine("Send completed. Press any key to exit client.");
Console.ReadKey(true);
NetworkComms.Shutdown();
}
}
}
For the server:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using NetworkCommsDotNet;
namespace Server
{
class Program
{
static void Main(string[] args)
{
NetworkComms.AppendGlobalIncomingPacketHandler<object>("ThreadIdRequest", (packetHeader, connection, incomingPlayer) =>
{
//Reply with the requested threadIds
Console.WriteLine("Received thread ID request from {0}.", connection.ToString());
connection.SendObject("ThreadIds", TaskManager().ToArray());
});
//Start listening for incoming TCP Connections
TCPConnection.StartListening(true);
Console.WriteLine("Server ready. Press any key to shutdown server.");
Console.ReadKey(true);
NetworkComms.Shutdown();
}
private static IEnumerable<string> TaskManager()
{
List<string> lst = new List<string>();
foreach (System.Diagnostics.Process p in
Process.GetProcesses().OrderBy(o => o.ProcessName))
{
lst.Add(p.ProcessName + "_" + p.Id);
}
return lst.AsParallel();
}
}
}
You will obviously need to download the NetworkComms.Net DLL from the website so that you can add it in the 'using NetworkCommsDotNet' reference. Also see the server IP address in the client example is currently "127.0.0.1", this should work if you run both the server and client on the same machine.
Disclaimer: I'm one of the developers for this library.
If you want a reliable and robust solution, use WCF instead of implementing serialization yourself.
Also, writing to stream from parallel threads would not work correctly. Parts of lines from different threads would mix together to some intangible garbage:
Parallel.ForEach(
TaskManager(), item=>
sw.WriteLine(item)
);
I am trying to send a message between 2 computers. I have been able to establish connection but for some weird reason i have been unable to acquire stream.
Server Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace chat_server
{
class Program
{
static void Main(string[] args)
{
TcpListener server = new TcpListener(IPAddress.Any, 9999);
server.Start();
Console.WriteLine("Waiting for client connections");
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("Client request accepted");
NetworkStream stream = client.GetStream();
StreamReader reader = new StreamReader(stream);
StreamWriter writer = new StreamWriter(stream);
Console.WriteLine("The message is " + reader.ReadToEnd());
}
}
}
Client Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace chat_client
{
class Program
{
static void Main(string[] args)
{
TcpClient client = new TcpClient("localhost", 9999);
NetworkStream stream = client.GetStream();
StreamReader reader = new StreamReader(stream);
StreamWriter writer = new StreamWriter(stream);
writer.Write("Hello world");
Console.WriteLine("Message Sent");
Console.ReadKey();
}
}
}
My server code confirms client connection by printing client request accepted. However for some reason i am unable to acquire data from stream. Quick Help would be really appreciated.
Thank you
You need to flush the stream in order to actually send the data.
Try:
writer.Write("Hello world");
writer.Flush();
Take a look at the MSDN docs for more information:
Synchronous socket server: http://msdn.microsoft.com/en-us/library/6y0e13d3.aspx
Asynchronous socket server: http://msdn.microsoft.com/en-us/library/5w7b7x5f.aspx
Here's a site that explains in more detail the ins and outs of sockets: http://nitoprograms.blogspot.co.uk/2009/04/tcpip-net-sockets-faq.html
In server side,
add static TcpListener server; at the top
Then `server.Start();
Socket soc = listener.AcceptSocket();
Console.WriteLine("Connection successful");
Stream s = new NetworkStream(soc);
StreamReader reader = new StreamReader(s);
StreamWriter writer= new StreamWriter(s);
sw.AutoFlush = true;
sw.WriteLine("hello world");`
In client side
TcpClient client = new TcpClient("localhost", 9999);
Stream s = client.GetStream();
Console.WriteLine("Connection successfully received");
StreamWriter writer = new StreamWriter(s);
StreamReader reader = new StreamReader(s);
sw.AutoFlush = true;
string dis=reader.readLine();
Console.WriteLine(dis);
Hope it will work now.