i need to send a list of errors trough tcpclient with this code
private bool TryConn(out TcpClient cliente)
{
var client = new TcpClient();
try
{
client.Connect(IpAddress, PortNumber);
cliente = client;
return true;
}
catch
{
cliente = null;
return false;
}
}
public void ProcesssRecovery()
{
//NonMassiveErrorerror= new NonMassiveError();
TcpClient client;
//get error
IEnumerable<NonMassiveError> errorNotNotified = GetUncheckedNonMassiveError();
//check if lista is not empty
if (errorNotNotified .Count() >0 )
{
// check connection
if (TryConn(out client))
{
foreach (NonMassiveError error in errorNotNotified )
{ // sending error<--how detect conn stops
SendMessage(error, client, "asin" , "");
error.Save();
}
}
}
//stop thread 10mins
else
{
Thread.Sleep(TimeSpan.FromMinutes(10));
}
}
}
How can i check if the connection falls down in the foreach to stop sending data??
You should wait a little bit and read the response to the data you sent.
If the server acknowledge then it's ok(Response = 0), if not stop sending.
SendMessage() is your own method, right? Check if socket.Send() in it returns 0 bytes. That means that the socket have gotten disconnected.
Related
I'm trying to use generic IP socket objects in .NET. Connecting one of my sockets to another one and sending a few ping-pong messages works just fine.
But now I've implemented a server socket and somehow managed to create Schrödinger's socket.
The server socket handles the incoming connection request just fine, but the connecting socket seems to be connected when I check once but doesn't even have a RemoteEndpoint - though is showing "Connected = true" and it's disconnected when I check the 2nd time.
All sockets are initialized using this function:
private void initSocket() {
if (_socket != null) {
_socket.Dispose();
}
_socket = new Socket(_ownIP.AddressFamily, _socketType, _protocolType);
_socket.Bind(new IPEndPoint(_ownIP, _ownPort));
}
The _socketType is Stream and the _protocolType is Tcp in this case.
The connect function:
public bool connect(string ip, int port, int retries = 3) {
if (_isWaiting) {
throw new InvalidOperationException("Can't connect, when the socket is already waiting for clients.");
}
_wasWaiting = false;
initSocket();
_otherIP = IPAddress.Parse(ip);
_otherPort = port;
IPEndPoint endpoint = new IPEndPoint(_otherIP, _otherPort);
for (int i = 0; i < retries; i++) {
try {
IAsyncResult result = _socket.BeginConnect(endpoint, null, null);
bool success = result.AsyncWaitHandle.WaitOne(_connectTimeout, true);
if (_socket.Connected) {
_socket.EndConnect(result);
_isConnected = true;
return true;
}
else {
_socket.EndConnect(result);
}
}
catch (SocketException e) {
Console.WriteLine(e.Message);
}
}
return false;
}
The server uses this wait function to accept more clients:
private void wait() {
while (_isWaiting) {
try {
_socket.Listen(1);
var newSocket = _socket.Accept();
var newClient = new IPSocket(newSocket, _socketType, _protocolType, new MessageReceivedDelegate(clientReceivedMessage), true);
_clients.Add(newClient.PartnerIP + ":" + newClient.PartnerPort, newClient);
clientConnected(newClient.PartnerIP, newClient.PartnerPort);
while (true) {
Thread.Sleep(10);
}
}
catch (System.Net.Sockets.SocketException ex) {
//Need to find out which one is thrown when no client for accepting available
if (ex.ErrorCode == 2) {
}
Thread.Sleep(10);
}
catch {
Thread.Sleep(10);
}
}
}
The newSocket variable is connected at first and has a valid RemoteEndpoint, but the socket that's connected doesn't route back to newSocket.
I thought that maybe the connection resets when the server socket tries to accept new sockets, so I added the infinite wait loop - which didn't help.
So why does the connecting socket not have any RemoteEndpoint? (it's shown as null)
Since people usually recommend to accept the connection, handle data, close connection:
I want to keep the connection alive and I have to - not doing this is at the time not a solution that I'd consider.
I am working on a transportation Project RailWay. Let me explain the project :there are a lot of sensors during the path of the train ,so when the train passes one of these sensors ,the sensor sends a value to CTC(A computer that manages the sensors) server the value is 1 or 0 ,1 means that the train arrive the sensor and 0 means the train left the sensor ,so every thing is ok ,now here is the scope of my project:
The CTC server send the value to MY-SERVER for example :ID=16(SENSOR-ID),state=0.it means that the train left the sensor that its id is 16 ,Note:That i know the location of sensors by id .so My problems start here : the CTC server sends its data by TCP ,so i have to create a listener to listen the data that comes from the CTC server ,(Note:Sometimes the data that comes from CTC is a lot and maybe some data be lost) ,I create a program using c# that listen the port but sometimes the data that coes fro CTC are lost why ?
So let me explain my programs:
It's the code that i wrote to get the data :
class Server
{
private TcpListener tcpListener;
private Thread listenThread;
public Server()
{
this.tcpListener = new TcpListener(IPAddress.Any, 3456);
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
this.tcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this.tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
//message has successfully been received
ASCIIEncoding encoder = new ASCIIEncoding();
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
}
tcpClient.Close();
}
}
And here i call the server class :
class Program
{
static void Main(string[] args)
{
Server obj=new Server();
}
}
The CTC code that sends data is like this(An example) :
static void Main(string[] args)
{
TcpClient client = new TcpClient();
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3456);
client.Connect(serverEndPoint);
using (NetworkStream clientStream = client.GetStream())
{
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("Hello Server!");
clientStream.Write(buffer, 0, buffer.Length);
}
}
But sometimes my programs(SERVER CODE) lost data ?!!!Why ?Any idea ?
Best regards
Here is my solution of a basic Client/Server application using StreamReader and StreamWriter
Basic structure
The server will be running a TcpListener. We will use the Pending() method to check for waiting connections while not blocking the thread from exiting. When a new connection is waiting we will accept it with the AcceptTcpClient(), create a new instance of our own Client class and add it to a List<Client()> to mange it later. The class Client will store the methods to send data and hold informations like ID etc.
Code
Server Class:
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace CTCServer
{
class Server
{
//Stores the IP Adress the server listens on
private IPAddress ip;
//Stores the port the server listens on
private int port;
//Stores the counter of connected clients. *Note* The counter only gets increased, it acts as "id"
private int clientCount = 0;
//Defines if the server is running. When chaning to false the server will stop and disconnect all clients.
private bool running = true;
//Stores all connected clients.
public List<Client> clients = new List<Client>();
//Event to pass recived data to the main class
public delegate void GotDataFromCTCHandler(object sender, string msg);
public event GotDataFromCTCHandler GotDataFromCTC;
//Constructor for Server. If autoStart is true, the server will automaticly start listening.
public Server(IPAddress ip, int port, bool autoStart = false)
{
this.ip = ip;
this.port = port;
if (autoStart)
this.Run();
}
//Starts the server.
public void Run()
{
//Run in new thread. Otherwise the whole application would be blocked
new Thread(() =>
{
//Init TcpListener
TcpListener listener = new TcpListener(this.ip, this.port);
//Start listener
listener.Start();
//While the server should run
while (running)
{
//Check if someone wants to connect
if (listener.Pending())
{
//Client connection incoming. Accept, setup data incoming event and add to client list
Client client = new Client(listener.AcceptTcpClient(), this.clientCount);
//Declare event
client.internalGotDataFromCTC += GotDataFromClient;
//Add to list
clients.Add(client);
//Increase client count
this.clientCount++;
}
else
{
//No new connections. Sleep a little to prevent CPU from going to 100%
Thread.Sleep(100);
}
}
//When we land here running were set to false or another problem occured. Stop server and disconnect all.
Stop();
}).Start(); //Start thread. Lambda \(o.o)/
}
//Fires event for the user
private void GotDataFromClient(object sender, string data)
{
//Data gets passed to parent class
GotDataFromCTC(sender, data);
}
//Send string "data" to all clients in list "clients"
public void SendToAll(string data)
{
//Call send method on every client. Lambda \(o.o)/
this.clients.ForEach(client => client.Send(data));
}
//Stop server
public void Stop()
{
//Exit listening loop
this.running = false;
//Disconnect every client in list "client". Lambda \(o.o)/
this.clients.ForEach(client => client.Close());
//Clear clients.
this.clients.Clear();
}
}
}
Client Class
using System.IO;
using System.Net.Sockets;
using System.Threading;
namespace CTCServer
{
class Client
{
//Stores the TcpClient
private TcpClient client;
//Stores the StreamWriter. Used to write to client
private StreamWriter writer;
//Stores the StreamReader. Used to recive data from client
private StreamReader reader;
//Defines if the client shuld look for incoming data
private bool listen = true;
//Stores clientID. ClientID = clientCount on connection time
public int id;
//Event to pass recived data to the server class
public delegate void internalGotDataFromCTCHandler(object sender, string msg);
public event internalGotDataFromCTCHandler internalGotDataFromCTC;
//Constructor
public Client(TcpClient client, int id)
{
//Assain members
this.client = client;
this.id = id;
//Init the StreamWriter
writer = new StreamWriter(this.client.GetStream());
reader = new StreamReader(this.client.GetStream());
new Thread(() =>
{
Listen(reader);
}).Start();
}
//Reads data from the connection and fires an event wih the recived data
public void Listen(StreamReader reader)
{
//While we should look for new data
while(listen)
{
//Read whole lines. This will read from start until \r\n" is recived!
string input = reader.ReadLine();
//If input is null the client disconnected. Tell the user about that and close connection.
if (input == null)
{
//Inform user
input = "Client with ID " + this.id + " disconnceted.";
internalGotDataFromCTC(this, input);
//Close
Close();
//Exit thread.
return;
}
internalGotDataFromCTC(this, input);
}
}
//Sends the string "data" to the client
public void Send(string data)
{
//Write and flush data
writer.WriteLine(data);
writer.Flush();
}
//Closes the connection
public void Close()
{
//Stop listening
listen = false;
//Close streamwriter FIRST
writer.Close();
//Then close connection
client.Close();
}
}
}
Test code. Note: this is a console application!
using System;
using System.Net;
namespace CTCServer
{
class Program
{
static void Main(string[] args)
{
//Set title
Console.Title = "CTC-Server";
//Create new instance of the server
Server server = new Server(IPAddress.Any, 1221);
//Handle GotDataFromCTC
server.GotDataFromCTC += GotDataFromCTC;
//Start the server. We could use the autoStart in constructor too.
server.Run();
//Inform about the running server
Console.WriteLine("Server running");
//Listen for input.
while(true)
{
//Read input line from cmd
string input = Console.ReadLine();
//Stores the command itself
string command;
//Stores parameters
string param = "";
//If input line contains a whitespace we have parameters that need to be processed.
if(input.Contains(" "))
{
//Split the command from the parameter. Parte before whitespace = command, rest = parameters
command = input.Split(' ')[0];
param = input.Substring(command.Length +1);
}
else
{
//No whitespace, so we dont have any parameters. Use whole input line as command.
command = input;
}
//Process the command
switch(command)
{
//Sends a string to all clients. Everything behind "send " (Note the whitespace) will be send to the client. Exanple "send hello!" will send "hello!" to the client.
case "send":
{
//Give some feedback
Console.WriteLine("Send to all clients: {0}", param);
//Send data
server.SendToAll(param);
//Done
break;
}
//Closes connection to all clients and exits. No parameters.
case "exit":
{
//Stop the server. This will disconncet all clients too.
server.Stop();
//Clean exit
Environment.Exit(0);
//Done. We wont get here anyway.
break;
}
}
}
}
//Recived data from clien. Show it!
static void GotDataFromCTC(object sender, string data)
{
Console.WriteLine("Data from CTC-Server with ID {0} recived:\r\n{1}", (sender as Client).id, data);
}
}
}
NOTE that this application doesnt have any exception handling. I did this to show a direction, you will need to modify the code to fit to your requirements. Let me know if you need something.
Example Project (Visual Studio 2013 Pro): Download | Virustoal
I found an article about Socket programming that using StreamReader ,It using one thread ,and no information lost is happened
You can take a look here :
http://www.codeproject.com/Articles/511814/Multi-client-per-one-server-socket-programming-in
I am new to programming and I am working on an asynchronous client server application.
I can send a message to the server from the client, but when I receive the data to the server (OnDataReceived Method) and try to send the same data back to the client (for testing purposes) I can't.
Not sure what other info I need to give, so please let me know, I don't mean to be vague.
SERVER CODE
public void OnDataReceived(IAsyncResult asyncResult)
{
try
{
SocketPacket socketData = (SocketPacket)asyncResult.AsyncState;
int iRx = 0;
iRx = socketData.currentSocket.EndReceive(asyncResult);
char[] chars = new char[iRx];
Decoder decoder = Encoding.UTF8.GetDecoder();
int charLen = decoder.GetChars(socketData.dataBuffer, 0, iRx, chars, 0);
String receivedData = new String(chars);
//BroadCast(receivedData);
this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => lbxMessages.Items.Add(receivedData)));
//Updated Code
this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() => broadcast(receivedData)));
WaitForData(socketData.currentSocket);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\n OnDataRecieved: Socket has been closed\n");
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}
public class SocketPacket
{
public Socket currentSocket;
public byte[] dataBuffer = new byte[50];//allowing the 50 digist to be sent at once
}
private void WaitForData(Socket socket)
{
try
{
if (workerCallBack == null)
{
workerCallBack = OnDataReceived;
}
SocketPacket sckPack = new SocketPacket();
sckPack.currentSocket = socket;
socket.BeginReceive(sckPack.dataBuffer, 0, sckPack.dataBuffer.Length, SocketFlags.None, workerCallBack, sckPack);
}
catch(SocketException se)
{
MessageBox.Show(se.Message);
}
}
Updated in response to Andrew's reply
I have a method that will be invoked when a client is connected
private void OnClientConnect(IAsyncResult asyncResult)
{
try
{
//Here we complete/end the Beginaccept() asynchronous call by
//calling EndAccept() - which returns the reference to a new socket object
workerSocket[clientCount] = listenSocket.EndAccept(asyncResult);
//Let the worker socket do the further processing for the just connected client
WaitForData(workerSocket[clientCount]);
//Now increment the client count
++clientCount;
if (clientCount<4)//allow max 3 clients
{
//Adds the connected client to the list
connectedClients.Add(listenSocket);
String str = String.Format("Client # {0} connected", clientCount);
this.Dispatcher.Invoke((Action)(() =>
{
//Display this client connection as a status message on the GUI
lbxMessages.Items.Add(str);
lblConnectionStatus.Content =clientCount + " Connected";
}));
//Since the main Socket is now free, it can go back and wait for
//other clients who are attempting to connect
listenSocket.BeginAccept(OnClientConnect, null);
}
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\n OnClientConnection: Socket has been closed\n");
}
catch (SocketException)
{
HandleClientDisconnect(listenSocket);
}
}
(UPDATED)
ADDED METHOD TO BROADCAST MESSAGE RECEIVED BY SERVER BACK TO CLIENT
public void broadcast(string msg)
{
//foreach (Socket item in connectedClients)
//{
Socket broadcastSocket;
broadcastSocket = workerSocket[0]; //sends message to first client connected
byte[] broadcastBytes = null;
broadcastBytes = Encoding.ASCII.GetBytes(msg);
broadcastSocket.Send(broadcastBytes);
//}
}
There is two sockets involved in server-side TCP communication. First socket is a listening socket and you should use it only for accepting new request. Then every time you accept new request from the client you're getting another socket for every connection.
You trying to send data back via listening socket but not via socket that you accepted.
Sergey has it right. If you're looking to have one server handle multiple clients, then you'll need some sort of ServerTerminal class which can listen for new connections and then setup some sort of "connectedclient" class to handle IO to that socket. Your "OnDataReceived" method would be in the connectedclient class.
In your socket accept routine it should look something like:
private void OnClientConnection(IAsyncResult asyn)
{
if (socketClosed)
{
return;
}
try
{
Socket clientSocket = listenSocket.EndAccept(asyn);
ConnectedClient connectedClient = new ConnectedClient(clientSocket, this, _ServerTerminalReceiveMode);
connectedClient.StartListening();
In the accept routine you're passed a socket - i've named this "clientSocket". This is the socket you want to write to, not the listening socket.
I've read a couple of posts on SignalR and thought for a fun test project that I could create a web application to poll my onkyo receiver for status and display the results in a browser. For an initial test, I was successfully able to send the current time on the server back to the client by using this code in Application_Start:
ThreadPool.QueueUserWorkItem(_ =>
{
dynamic clients = Hub.GetClients<KudzuHub>();
while (true)
{
clients.addMessage(DateTime.Now.ToString());
Thread.Sleep(1000);
}
});
In the client javascript, i have the following code:
// Proxy created on the fly
var kHub = $.connection.kudzuHub;
// Declare a function on the hub so that the server can invoke it
kHub.addMessage = function (message) {
console.log('message added');
$('#messages').append('<li>' + message + '</li>');
};
// start the connection
$.connection.hub.start();
So all of that works fine. Every second, I get a new list item containing the current server date and time.
Now when I add this code to read data from the Onkyo receiver, it breaks: (still in Application_Start)
ThreadPool.QueueUserWorkItem(_ =>
{
dynamic clients = Hub.GetClients<KudzuHub>();
try
{
while (true)
{
string host = ConfigurationManager.AppSettings["receiverIP"].ToString();
int port = Convert.ToInt32(ConfigurationManager.AppSettings["receiverPort"]);
TcpClient tcpClient = new TcpClient(host, port);
NetworkStream clientSockStream = tcpClient.GetStream();
byte[] bytes = new byte[tcpClient.ReceiveBufferSize];
clientSockStream.Read(bytes, 0, (int)tcpClient.ReceiveBufferSize);
tcpClient.Close();
clients.addMessage(System.Text.Encoding.ASCII.GetString(bytes));
Thread.Sleep(50);
}
}
catch (SocketException ex)
{
// do something to handle the error
}
});
I set a break point and stepped through the code. It gets to this line and then returns.
clientSockStream.Read(bytes, 0, (int)tcpClient.ReceiveBufferSize);
It never finishes the rest of the code to send the message to the client. What am I doing wrong?
Thanks.
I would make some structural changes to your loop to allow the receiver time to respond, remove the overhead of retrieving the configuration every 50 milliseconds, and cleanup the open network stream:
ThreadPool.QueueUserWorkItem(_ =>
{
dynamic clients = Hub.GetClients<KudzuHub>();
TcpClient tcpClient = null;
NetworkStream clientSockStream = null;
try
{
string host = ConfigurationManager.AppSettings["receiverIP"].ToString();
int port = Convert.ToInt32(ConfigurationManager.AppSettings["receiverPort"]);
while (true)
{
if (tcpClient == null) {
tcpClient = new TcpClient(host, port);
clientSockStream = tcpClient.GetStream();
}
if (clientSockStream.CanRead) {
byte[] bytes = new byte[tcpClient.ReceiveBufferSize];
try {
clientSockStream.Read(bytes, 0, (int)tcpClient.ReceiveBufferSize);
} catch (Exception ex) {
// Add some debug code here to examine the exception that is thrown
}
tcpClient.Close();
// Closing the client does not automatically close the stream
clientSockStream.Close();
tcpClient = null;
clientSockStream = null;
clients.addMessage(System.Text.Encoding.ASCII.GetString(bytes));
}
Thread.Sleep(50);
}
}
catch (SocketException ex)
{
// do something to handle the error
} finally {
if (tcpClient != null) {
tcpClient.Close();
clientSockStream.Close();
}
}
});
I need to develop a service that will connect to a TCP server. Main tasks are reading incoming messages and also sending commands to the server in ten minutes, like a synchronize command. For example, I used the TcpClient object as shown below:
...
TcpClient tcpClient = new TcpClient();
tcpClient.Connect("x.x.x.x", 9999);
networkStream = tcpClient.GetStream();
clientStreamReader = new StreamReader(networkStream);
clientStreamWriter = new StreamWriter(networkStream);
while(true)
{
clientStreamReader.Read()
}
Also, when I need to write out something in any method, I use:
clientStreamWriter.write("xxx");
Is this usage correct? Or is there a better way?
First, I recommend that you use WCF, .NET Remoting, or some other higher-level communication abstraction. The learning curve for "simple" sockets is nearly as high as WCF, because there are so many non-obvious pitfalls when using TCP/IP directly.
If you decide to continue down the TCP/IP path, then review my .NET TCP/IP FAQ, particularly the sections on message framing and application protocol specifications.
Also, use asynchronous socket APIs. The synchronous APIs do not scale and in some error situations may cause deadlocks. The synchronous APIs make for pretty little example code, but real-world production-quality code uses the asynchronous APIs.
Be warned - this is a very old and cumbersome "solution".
By the way, you can use serialization technology to send strings, numbers or any objects which are support serialization (most of .NET data-storing classes & structs are [Serializable]).
There, you should at first send Int32-length in four bytes to the stream and then send binary-serialized (System.Runtime.Serialization.Formatters.Binary.BinaryFormatter) data into it.
On the other side or the connection (on both sides actually) you definetly should have a byte[] buffer which u will append and trim-left at runtime when data is coming.
Something like that I am using:
namespace System.Net.Sockets
{
public class TcpConnection : IDisposable
{
public event EvHandler<TcpConnection, DataArrivedEventArgs> DataArrive = delegate { };
public event EvHandler<TcpConnection> Drop = delegate { };
private const int IntSize = 4;
private const int BufferSize = 8 * 1024;
private static readonly SynchronizationContext _syncContext = SynchronizationContext.Current;
private readonly TcpClient _tcpClient;
private readonly object _droppedRoot = new object();
private bool _dropped;
private byte[] _incomingData = new byte[0];
private Nullable<int> _objectDataLength;
public TcpClient TcpClient { get { return _tcpClient; } }
public bool Dropped { get { return _dropped; } }
private void DropConnection()
{
lock (_droppedRoot)
{
if (Dropped)
return;
_dropped = true;
}
_tcpClient.Close();
_syncContext.Post(delegate { Drop(this); }, null);
}
public void SendData(PCmds pCmd) { SendDataInternal(new object[] { pCmd }); }
public void SendData(PCmds pCmd, object[] datas)
{
datas.ThrowIfNull();
SendDataInternal(new object[] { pCmd }.Append(datas));
}
private void SendDataInternal(object data)
{
if (Dropped)
return;
byte[] bytedata;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
try { bf.Serialize(ms, data); }
catch { return; }
bytedata = ms.ToArray();
}
try
{
lock (_tcpClient)
{
TcpClient.Client.BeginSend(BitConverter.GetBytes(bytedata.Length), 0, IntSize, SocketFlags.None, EndSend, null);
TcpClient.Client.BeginSend(bytedata, 0, bytedata.Length, SocketFlags.None, EndSend, null);
}
}
catch { DropConnection(); }
}
private void EndSend(IAsyncResult ar)
{
try { TcpClient.Client.EndSend(ar); }
catch { }
}
public TcpConnection(TcpClient tcpClient)
{
_tcpClient = tcpClient;
StartReceive();
}
private void StartReceive()
{
byte[] buffer = new byte[BufferSize];
try
{
_tcpClient.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, DataReceived, buffer);
}
catch { DropConnection(); }
}
private void DataReceived(IAsyncResult ar)
{
if (Dropped)
return;
int dataRead;
try { dataRead = TcpClient.Client.EndReceive(ar); }
catch
{
DropConnection();
return;
}
if (dataRead == 0)
{
DropConnection();
return;
}
byte[] byteData = ar.AsyncState as byte[];
_incomingData = _incomingData.Append(byteData.Take(dataRead).ToArray());
bool exitWhile = false;
while (exitWhile)
{
exitWhile = true;
if (_objectDataLength.HasValue)
{
if (_incomingData.Length >= _objectDataLength.Value)
{
object data;
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(_incomingData, 0, _objectDataLength.Value))
try { data = bf.Deserialize(ms); }
catch
{
SendData(PCmds.Disconnect);
DropConnection();
return;
}
_syncContext.Post(delegate(object T)
{
try { DataArrive(this, new DataArrivedEventArgs(T)); }
catch { DropConnection(); }
}, data);
_incomingData = _incomingData.TrimLeft(_objectDataLength.Value);
_objectDataLength = null;
exitWhile = false;
}
}
else
if (_incomingData.Length >= IntSize)
{
_objectDataLength = BitConverter.ToInt32(_incomingData.TakeLeft(IntSize), 0);
_incomingData = _incomingData.TrimLeft(IntSize);
exitWhile = false;
}
}
StartReceive();
}
public void Dispose() { DropConnection(); }
}
}
That is just an example, you should edit it for your use.
I have had luck using the socket object directly (rather than the TCP client). I create a Server object that looks something like this (I've edited some stuff such as exception handling out for brevity, but I hope that the idea comes across.)...
public class Server()
{
private Socket sock;
// You'll probably want to initialize the port and address in the
// constructor, or via accessors, but to start your server listening
// on port 8080 and on any IP address available on the machine...
private int port = 8080;
private IPAddress addr = IPAddress.Any;
// This is the method that starts the server listening.
public void Start()
{
// Create the new socket on which we'll be listening.
this.sock = new Socket(
addr.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);
// Bind the socket to the address and port.
sock.Bind(new IPEndPoint(this.addr, this.port));
// Start listening.
this.sock.Listen(this.backlog);
// Set up the callback to be notified when somebody requests
// a new connection.
this.sock.BeginAccept(this.OnConnectRequest, sock);
}
// This is the method that is called when the socket recives a request
// for a new connection.
private void OnConnectRequest(IAsyncResult result)
{
// Get the socket (which should be this listener's socket) from
// the argument.
Socket sock = (Socket)result.AsyncState;
// Create a new client connection, using the primary socket to
// spawn a new socket.
Connection newConn = new Connection(sock.EndAccept(result));
// Tell the listener socket to start listening again.
sock.BeginAccept(this.OnConnectRequest, sock);
}
}
Then, I use a separate Connection class to manage the individual connection with the remote host. That looks something like this...
public class Connection()
{
private Socket sock;
// Pick whatever encoding works best for you. Just make sure the remote
// host is using the same encoding.
private Encoding encoding = Encoding.UTF8;
public Connection(Socket s)
{
this.sock = s;
// Start listening for incoming data. (If you want a multi-
// threaded service, you can start this method up in a separate
// thread.)
this.BeginReceive();
}
// Call this method to set this connection's socket up to receive data.
private void BeginReceive()
{
this.sock.BeginReceive(
this.dataRcvBuf, 0,
this.dataRcvBuf.Length,
SocketFlags.None,
new AsyncCallback(this.OnBytesReceived),
this);
}
// This is the method that is called whenever the socket receives
// incoming bytes.
protected void OnBytesReceived(IAsyncResult result)
{
// End the data receiving that the socket has done and get
// the number of bytes read.
int nBytesRec = this.sock.EndReceive(result);
// If no bytes were received, the connection is closed (at
// least as far as we're concerned).
if (nBytesRec <= 0)
{
this.sock.Close();
return;
}
// Convert the data we have to a string.
string strReceived = this.encoding.GetString(
this.dataRcvBuf, 0, nBytesRec);
// ...Now, do whatever works best with the string data.
// You could, for example, look at each character in the string
// one-at-a-time and check for characters like the "end of text"
// character ('\u0003') from a client indicating that they've finished
// sending the current message. It's totally up to you how you want
// the protocol to work.
// Whenever you decide the connection should be closed, call
// sock.Close() and don't call sock.BeginReceive() again. But as long
// as you want to keep processing incoming data...
// Set up again to get the next chunk of data.
this.sock.BeginReceive(
this.dataRcvBuf, 0,
this.dataRcvBuf.Length,
SocketFlags.None,
new AsyncCallback(this.OnBytesReceived),
this);
}
}
You can use your Connection object to send data by calling its Socket directly, like so...
this.sock.Send(this.encoding.GetBytes("Hello to you, remote host."));
As I said, I've tried to edit the code here for posting, so I apologize if there are any errors in it.
First of all, TCP does not guarantee that everything that you send will be received with the same read at the other end. It only guarantees that all bytes that you send will arrive and in the correct order.
Therefore, you will need to keep building up a buffer when reading from the stream. You will also have to know how large each message is.
The simplest ever is to use a non-typeable ASCII character to mark the end of the packet and look for it in the received data.
I've developed a dotnet library that might come in useful. I have fixed the problem of never getting all of the data if it exceeds the buffer, which many posts have discounted. Still some problems with the solution but works descently well https://github.com/Apollo013/DotNet-TCP-Communication