I'm building a simple server, that contains some information and that will be using Async Sockets to get N Connections (I don't really know how many, they can range from 50 to 1000).
The main purpose of this server is to 'verify' the file integrity and version of clients connecting, and if needed send them the updated files so they can patch.
Right now, I'm on the part of getting my sockets running, I can display the data in the console, and everything is fine, problem is, my most important variables are on my 'DownloadServer' class, while my socket functions are on a class named 'ServerSocket'.
My question is, how can I access the data in DownloadServer(that contains an instance of ServerSocket) when said info is requested to my socket. I can't return the data received with Async Callbacks (because they must be void), so what would be the best way to go about this?
Here's my socket code, it only receives a client and prints data sent. What I want to do is acess the received info (likewise, a command for example) and return to the client the request info, for example, if the message received is "GetFileVersion", the server should reply back with the value of the variable it contains (it's a global variable named FILEVERSION), being the problem that I can't seem to find a way to acces that variable.
class ServerSocket
{
private Socket _socket;
private byte[] _buffer;
public ServerSocket()
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public void bind(int port)
{
_socket.Bind(new IPEndPoint(IPAddress.Any, port));
}
public void Listen(int backlog)
{
_socket.Listen(backlog);
}
public void Accept()
{
_socket.BeginAccept(AcceptCallBack, null);
}
private void AcceptCallBack(IAsyncResult ar)
{
Socket clientSocket = _socket.EndAccept(ar);
Accept();
IPEndPoint clientEndPoint = (IPEndPoint)clientSocket.RemoteEndPoint;
Console.WriteLine("Client Connected: {0}",clientEndPoint.Address.ToString());
_buffer = new byte[2048];
clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, clientSocket);
}
private void ReceivedCallback(IAsyncResult ar)
{
try
{
Socket clientSocket = ar.AsyncState as Socket;
int size = clientSocket.EndReceive(ar);
byte[] packet = new byte[size];
Array.Copy(_buffer, packet, packet.Length);
String data = System.Text.Encoding.ASCII.GetString(packet, 0, packet.Length);
}
catch (Exception ex)
{
;
}
}
}
}
Thank you in advance!
EDIT:
What I'd like to do is something like String commandReceived = socket.Receive() (like you would do with synchronous sockets), or some way to acess that command received via that socket.
class DownloadServer
{
private static String UPDATEPATH="";
private static int FILEVERSION;
private static string ROOTPATH = "";
private String SQLUsername = "";
private String SQLPassword = "";
private String AccountDB = "";
private String ServerName = "";
private SqlConnection sqlConn;
//some logic methods
static void Main(string[] args){
ServerSecurity serverSecurity = new ServerSecurity();
ServerSocket serverSocket = new ServerSocket();
serv.Initialize();
serverSecurity.Initialize();
serverSocket.bind(15779);
serverSocket.Listen(1000);
serverSocket.Accept();
while (true)
{
Console.WriteLine("Input the command:");
var Input = Console.ReadLine();
if (Input == "Update")
{
serv.prepareUpdate();
}
else if (Input == "Exit")
{
Environment.Exit(0);
}
else if (Input == "TestEncryption")
{
Console.WriteLine("Enter the text to be encrypted:");
String plainText = Console.ReadLine();
Console.WriteLine("Original Text = {0}", plainText);
Console.WriteLine("Encrypted Text = {0}", serverSecurity.Encrypt(plainText));
Console.WriteLine("Decrypted Text = {0}", serverSecurity.Decrypt(serverSecurity.Encrypt(plainText)));
}
}
}
Add in your DownloadServer Class :
Socket NewSocket;
byte[] buffer = new buffer [1024];
NewSocket = serverSocket.Accept();
Then begin Receive from this NewSocket :
void Receive()
{
NewSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, r);
}
private void ReceiveCallback(IAsyncResult ar)
{
Socket CurrentClient = (Socket)ar.AsyncState;
int Received;
try { Received = CurrentClient.Receive(buffer); }
catch
{
return;
}
byte[] _buffer = new byte[Received];
Array.Copy(buffer, _buffer, Received);
string _Text = Encoding.Unicode.GetString(_buffer);
if(_Text == "GetVersion")
byte[] infoVersion = Encoding.Encoding.Default.GetBytes(FILEVERSION);
CurrentClient .Send(infoVersion )
}
This looks like a classic design problem where you are attempting to give a class too many responsibilities. In SOLID design, each class should have a single responsibility. The SocketServer would then only be responsible for creating the socket and sending/receiving data. Your DownloadServer would be responsible for creating data to be sent. Another class say ValidationServer, would be responsible for requesting data through the SocketServer and parsing it. A third class, a controller if you will, would be responsible for coordinating calls and passing data between these classes. In this way your SocketServer and DownloadServer would be decoupled and not require variables from each other.
Related
I have this code as a server application to receive data :
public AsyncCallback pfnWorkerCallBack;
private Socket m_mainSocket;
private Socket[] m_workerSocket = new Socket[25];
private int m_clientCount = 0;
public void startfun()
{
string Camera1Port = "1001";
int port1 = System.Convert.ToInt32(Camera1Port);
m_mainSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port1);
m_mainSocket.Bind(ipLocal);
m_mainSocket.Listen(20);
m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
public void OnClientConnect(IAsyncResult asyn)
{
m_workerSocket[m_clientCount] = m_mainSocket.EndAccept(asyn);
WaitForData(m_workerSocket[m_clientCount]);
++m_clientCount;
m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
public class SocketPacket
{
public System.Net.Sockets.Socket m_currentSocket;
public byte[] dataBuffer ;
}
public void WaitForData(System.Net.Sockets.Socket soc)
{
if (pfnWorkerCallBack == null)
{
pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
}
SocketPacket theSocPkt = new SocketPacket();
theSocPkt.dataBuffer = new byte[soc.ReceiveBufferSize];
theSocPkt.m_currentSocket = soc;
soc.BeginReceive(theSocPkt.dataBuffer, 0,
theSocPkt.dataBuffer.Length,
SocketFlags.None,
pfnWorkerCallBack,
theSocPkt);
}
public void OnDataReceived(IAsyncResult asyn)
{
SocketPacket socketData = (SocketPacket)asyn.AsyncState;
string res = GetParameters(socketData.dataBuffer);
MessageBox.Show(res);
WaitForData(socketData.m_currentSocket);
}
public string GetParameters(byte[] buf)
{
string result = System.Text.Encoding.UTF8.GetString(buf);
return result;
}
The problem is the data is breaking .I mean when i receive data from socket in first time all values are received and the messagebox show all of that.when i receive the second data ,i just receive some part of that .suppose the data is 870314854798 .I receive the data in first time correctly ,but in second i just get 3 or 4 digit of that(it is random) and the message box shows 3 or 4 digit after clicking of ok (Messagebox) the other digits are shown.why ?
i googled it and i found this but i can't map this solution with my solution
public static void Read_Callback(IAsyncResult ar){
StateObject so = (StateObject) ar.AsyncState;
Socket s = so.workSocket;
int read = s.EndReceive(ar);
if (read > 0) {
so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0, read));
s.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0,
new AsyncCallback(Async_Send_Receive.Read_Callback), so);
}
else{
if (so.sb.Length > 1) {
//All of the data has been read, so displays it to the console
string strContent;
strContent = so.sb.ToString();
Console.WriteLine(String.Format("Read {0} byte from socket" +
"data = {1} ", strContent.Length, strContent));
}
s.Close();
}
}
The problem is how can i merge this solution with my code ?
In your OnDataReceived method you should EndReceive to complete the operation you began like this:
StateObject so = (StateObject) async.AsyncState;
Socket s = so.workSocket;
int read = s.EndReceive(async); // You need to do this.
You should always call, as per MSDN's recommendations, the EndReceive, EndInvoke etc on any async operation. Otherwise, you may not get the exceptions either.
That's how network stack (especially TCP) works. It does not guarantee that all data sent into a socket as a single piece would be delivered to the client as a single piece. You can only expect that all data sent into a socket will be received by the client in the same order, but can't rely on how it is going to be split into chunks.
E.g. as the data travel through the network they might pass nodes that have different settings and limitations on how big the packet may be. If a packet is too large, it will be split into multiple packets of appropriate size that will be then sent to the client.
If your protocol requires that a packet is delivered from the server to the client as a single solid piece, you would have to construct your own application layer protocol to control that. This is how http works: no matter how the data representing a web page is split into TCP packets, the client receives the whole page at once.
This is my current setup (using UDP):
void OnDataReceived(IAsyncResult result)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
byte[] buffer = socket.EndReceive(result, ref ep);
Packet p = new Packet(Encoding.ASCII.GetString(buffer, 0, buffer.Length));
//process packet
socket.BeginReceive(new AsyncCallback(OnDataReceived), socket);
}
I was wondering what would happen if I immediately call socket.BeginReceive after calling EndReceive and then process the packet to obtain a continous packet flow like this:
void OnDataReceived(IAsyncResult result)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
byte[] buffer = socket.EndReceive(result, ref ep);
socket.BeginReceive(new AsyncCallback(OnDataReceived), socket);
Packet p = new Packet(Encoding.ASCII.GetString(buffer, 0, buffer.Length));
//process packets
}
If a packet is received as soon as I call BeginReceive, would this conflict with the current packet processing somehow?
Also if this would not conflict would changing to TCP make this disfunctional?
Looks like you are creating some sort of recursive handler there. I am unsure how that will work, probably not in a good way. I usually go for a separate reader thread that listens to incoming data and passes it on to an event. This has served me well in the past. I have not looked into using async for this though.
Here is some example code on how to use a separate thread to handle incoming UDP data. It is not complete but should give you an idea of how to set it up.
private Thread _udpReadThread;
private volatile bool _terminateThread;
public event DataEventHandler OnDataReceived;
public delegate void DataEventHandler(object sender, DataEventArgs e);
private void CreateUdpReadThread()
{
_udpReadThread = new Thread(UdpReadThread) { Name = "UDP Read thread" };
_udpReadThread.Start(new IPEndPoint(IPAddress.Any, 1234));
}
private void UdpReadThread(object endPoint)
{
var myEndPoint = (EndPoint)endPoint;
var udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
udpListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// Important to specify a timeout value, otherwise the socket ReceiveFrom()
// will block indefinitely if no packets are received and the thread will never terminate
udpListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 100);
udpListener.Bind(myEndPoint);
try
{
while (!_terminateThread)
{
try
{
var buffer = new byte[1024];
var size = udpListener.ReceiveFrom(buffer, ref myEndPoint);
Array.Resize(ref buffer, size);
// Let any consumer(s) handle the data via an event
FireOnDataReceived(((IPEndPoint)(myEndPoint)).Address, buffer);
}
catch (SocketException socketException)
{
// Handle socket errors
}
}
}
finally
{
// Close Socket
udpListener.Shutdown(SocketShutdown.Both);
udpListener.Close();
}
}
public class DataEventArgs : EventArgs
{
public byte[] Data { get; private set; }
public IPAddress IpAddress { get; private set; }
public DataEventArgs(IPAddress ipaddress, byte[] data)
{
IpAddress = ipaddress;
Data = data;
}
}
hey all,
I have made a socket server in C# for a flash game that I am developing, I got the code from somewhere and I am a beginner in c# and .net development . It works fine in practice when connections are made and the server functions correctly. Get 2 concurrent connections at the same time and we have a problem.
here is the basic aspects of the socket server below: (alot taken out for obvious reasons)
how can I alter this so that it can handle concurrent connections? Should I be threading each response?
Thanks
class TcpSock
{
int tcpIndx = 0;
int tcpByte = 0;
byte[] tcpRecv = new byte[1024];
////////////////////////////////////////
public Socket tcpSock;
////////////////////////////////////////
public int Recv(ref string tcpRead)
{
tcpByte = tcpSock.Available;
if (tcpByte > tcpRecv.Length - tcpIndx)
tcpByte = tcpRecv.Length - tcpIndx;
tcpByte = tcpSock.Receive(tcpRecv, tcpIndx, tcpByte,
SocketFlags.Partial);
tcpRead = Encoding.ASCII.GetString
(tcpRecv, tcpIndx, tcpByte);
tcpIndx += tcpByte;
return tcpRead.Length;
}
public int RecvLn(ref string tcpRead)
{
tcpRead = Encoding.ASCII.GetString
(tcpRecv, 0, tcpIndx);
tcpIndx = 0;
return tcpRead.Length;
}
public int Send(string tcpWrite)
{
return tcpSock.Send(Encoding.ASCII.GetBytes(tcpWrite));
}
public int SendLn(string tcpWrite)
{
return tcpSock.Send(Encoding.ASCII.GetBytes(tcpWrite + "\r\n"));
}
}
[STAThread]
static void Main()
{
Thread Server1 = new Thread(RunServer);
Server1.Start();
}
static void RunServer()
{
///class IPHostEntry : Stores information about the Host and is required
///for IPEndPoint.
///class IPEndPoint : Stores information about the Host IP Address and
///the Port number.
///class TcpSock : Invokes the constructor and creates an instance.
///class ArrayList : Stores a dynamic array of Client TcpSock objects.
IPHostEntry Iphe = Dns.Resolve(Dns.GetHostName());
IPEndPoint Ipep = new IPEndPoint(Iphe.AddressList[0], 4444);
Socket Server = new Socket(Ipep.Address.AddressFamily,SocketType.Stream, ProtocolType.Tcp);
///Initialize
///Capacity : Maximux number of clients able to connect.
///Blocking : Determines if the Server TcpSock will stop code execution
///to receive data from the Client TcpSock.
///Bind : Binds the Server TcpSock to the Host IP Address and the Port Number.
///Listen : Begin listening to the Port; it is now ready to accept connections.
ArrayList Client = new ArrayList();
string[,] Users = new string[1000,9];
string rln = null;
string[] Data;
Client.Capacity = 1000;
Server.Blocking = false;
Server.Bind(Ipep);
Server.Listen(32);
Console.WriteLine("Server 1 {0}: listening to port {1}", Dns.GetHostName(), Ipep.Port);
////////////////////////////////////////////////////////////////////////////////////////////
///Main loop
///1. Poll the Server TcpSock; if true then accept the new connection.
///2. Poll the Client TcpSock; if true then receive data from Clients.
while (true)
{
//Accept - new connection
#region new connection
if (Server.Poll(0, SelectMode.SelectRead))
{
int i = Client.Add(new TcpSock());
((TcpSock)Client[i]).tcpSock = Server.Accept();
Console.WriteLine("Client " + i + " connected.");
Users[i, 0] = i.ToString();
}
#endregion
for (int i = 0; i < Client.Count; i++)
{
//check for incoming data
if (((TcpSock)Client[i]).tcpSock.Poll(0, SelectMode.SelectRead))
{
//receive incoming data
if (((TcpSock)Client[i]).Recv(ref rln) > 0)
{
Console.WriteLine(rln.ToString());
Data = rln.Split('|');
// 1) initial connection
#region InitialConnection
if (Data[0] == "0000")
{
}
}
}
}
}
}
You will need to not use synchronous functions but asynchrounus functions like Socket.BeginReceive
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
public static partial class TcpServer
{
public static void Main()
{
// Setup listener on "localhost" port 12000
IPAddress ipAddr = Dns.GetHostEntry("localhost").AddressList[0];
TcpListener server = new TcpListener(ipAddr, 12000);
server.Start(); // Network driver can now allow incoming requests
// Accept up to 1 client per CPU simultaneously
Int32 numConcurrentClients = Environment.ProcessorCount;
for (Int32 n = 0; n
private static Byte[] ProcessData(Byte[] inputData)
{
String inputString = Encoding.UTF8.GetString(inputData, 1, inputData[0]);
String outputString = inputString.ToUpperInvariant();
Console.WriteLine("Input={0}", inputString);
Console.WriteLine(" Output={0}", outputString);
Console.WriteLine();
Byte[] outputStringBytes = Encoding.UTF8.GetBytes(outputString);
Byte[] outputData = new Byte[1 + outputStringBytes.Length];
outputData[0] = (Byte)outputStringBytes.Length;
Array.Copy(outputStringBytes, 0, outputData, 1, outputStringBytes.Length);
return outputData;
}
}
public static partial class TcpServer
{
private sealed class ClientConnectionApm
{
private TcpListener m_server;
private TcpClient m_client;
private Stream m_stream;
private Byte[] m_inputData = new Byte[1];
private Byte m_bytesReadSoFar = 0;
public ClientConnectionApm(TcpListener server)
{
m_server = server;
m_server.BeginAcceptTcpClient(AcceptCompleted, null);
}
private void AcceptCompleted(IAsyncResult ar)
{
// Connect to this client
m_client = m_server.EndAcceptTcpClient(ar);
// Accept another client
new ClientConnectionApm(m_server);
// Start processing this client
m_stream = m_client.GetStream();
// Read 1 byte from client which contains length of additional data
m_stream.BeginRead(m_inputData, 0, 1, ReadLengthCompleted, null);
}
private void ReadLengthCompleted(IAsyncResult result)
{
// If client closed connection; abandon this client request
if (m_stream.EndRead(result) == 0) { m_client.Close(); return; }
// Start to read 'length' bytes of data from client
Int32 dataLength = m_inputData[0];
Array.Resize(ref m_inputData, 1 + dataLength);
m_stream.BeginRead(m_inputData, 1, dataLength, ReadDataCompleted, null);
}
private void ReadDataCompleted(IAsyncResult ar)
{
// Get number of bytes read from client
Int32 numBytesReadThisTime = m_stream.EndRead(ar);
// If client closed connection; abandon this client request
if (numBytesReadThisTime == 0) { m_client.Close(); return; }
// Continue to read bytes from client until all bytes are in
m_bytesReadSoFar += (Byte)numBytesReadThisTime;
if (m_bytesReadSoFar
private void WriteDataCompleted(IAsyncResult ar)
{
// After result is written to client, close the connection
m_stream.EndWrite(ar);
m_client.Close();
}
}
}
First of all: Stop using non-blocking sockets. In .NET you should either stick to the synchronous methods Receive/Send or asynchronous methods BeginReceive/BeginSend.
You should only stick with sync methods if you will have only a handful of clients. Then launch each new client in a new thread. This is the easiest option to get everthing running.
Simply do like this:
public void AcceptClients()
{
TcpListener listener = new TcpListener(IPAddress.Any, 5566);
listener.Start();
while (_serverRunning)
{
var socket = listener.AcceptSocket();
new Thread(ClientFunc).Start(socket);
}
}
public void ClientFun(object state)
{
var clientSocket = (Socket)state;
var buffer = new byte[65535];
while (_serverRunning)
{
//blocking read.
clientSocket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
//check packet.
// handle packet
// send respons.
clientSocket.Send(alalalal);
}
}
You should refactor the methods so that they follow SRP. The code is just a small guide to get you going.
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
I'm trying to write a service that listens to a TCP Socket on a given port until an end of line is recived and then based on the "line" that was received executes a command.
I've followed a basic socket programming tutorial for c# and have come up with the following code to listen to a socket:
public void StartListening()
{
_log.Debug("Creating Maing TCP Listen Socket");
_mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, _port);
_log.Debug("Binding to local IP Address");
_mainSocket.Bind(ipLocal);
_log.DebugFormat("Listening to port {0}",_port);
_mainSocket.Listen(10);
_log.Debug("Creating Asynchronous callback for client connections");
_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
public void OnClientConnect(IAsyncResult asyn)
{
try
{
_log.Debug("OnClientConnect Creating worker socket");
Socket workerSocket = _mainSocket.EndAccept(asyn);
_log.Debug("Adding worker socket to list");
_workerSockets.Add(workerSocket);
_log.Debug("Waiting For Data");
WaitForData(workerSocket);
_log.DebugFormat("Clients Connected [{0}]", _workerSockets.Count);
_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
}
catch (ObjectDisposedException)
{
_log.Error("OnClientConnection: Socket has been closed\n");
}
catch (SocketException se)
{
_log.Error("Socket Exception", se);
}
}
public class SocketPacket
{
private System.Net.Sockets.Socket _currentSocket;
public System.Net.Sockets.Socket CurrentSocket
{
get { return _currentSocket; }
set { _currentSocket = value; }
}
private byte[] _dataBuffer = new byte[1];
public byte[] DataBuffer
{
get { return _dataBuffer; }
set { _dataBuffer = value; }
}
}
private void WaitForData(Socket workerSocket)
{
_log.Debug("Entering WaitForData");
try
{
lock (this)
{
if (_workerCallback == null)
{
_log.Debug("Initializing worker callback to OnDataRecieved");
_workerCallback = new AsyncCallback(OnDataRecieved);
}
}
SocketPacket socketPacket = new SocketPacket();
socketPacket.CurrentSocket = workerSocket;
workerSocket.BeginReceive(socketPacket.DataBuffer, 0, socketPacket.DataBuffer.Length, SocketFlags.None, _workerCallback, socketPacket);
}
catch (SocketException se)
{
_log.Error("Socket Exception", se);
}
}
public void OnDataRecieved(IAsyncResult asyn)
{
SocketPacket socketData = (SocketPacket)asyn.AsyncState;
try
{
int iRx = socketData.CurrentSocket.EndReceive(asyn);
char[] chars = new char[iRx + 1];
_log.DebugFormat("Created Char array to hold incomming data. [{0}]",iRx+1);
System.Text.Decoder decoder = System.Text.Encoding.UTF8.GetDecoder();
int charLength = decoder.GetChars(socketData.DataBuffer, 0, iRx, chars, 0);
_log.DebugFormat("Read [{0}] characters",charLength);
String data = new String(chars);
_log.DebugFormat("Read in String \"{0}\"",data);
WaitForData(socketData.CurrentSocket);
}
catch (ObjectDisposedException)
{
_log.Error("OnDataReceived: Socket has been closed. Removing Socket");
_workerSockets.Remove(socketData.CurrentSocket);
}
catch (SocketException se)
{
_log.Error("SocketException:",se);
_workerSockets.Remove(socketData.CurrentSocket);
}
}
This I thought was going to be a good basis for what I wanted to do, but the code I have appended the incoming characters to a text box one by one and didn't do anything with it. Which doesn't really work for what I want to do.
My main issue is the decoupling of the OnDataReceived method from the Wait for data method. which means I'm having issues building a string (I would use a string builder but I can accept multiple connections so that doesn't really work.
Ideally I'd like to look while listening to a socket until I see and end of line character and then call a method with the resulting string as a parameter.
What's the best way to go about doing this.
Try using asynch sockets. The code below will listening to a socket, and if the new line char through telnet is recieved it will echo it back out to the incomming socket. It seems like you would just need to redirect that input to your text box.
private string _hostName;
private const int _LISTENINGPORT = 23;
private Socket _incomingSocket;
byte[] _recievedData;
//todo: do we need 1024 byte? the asynch methods read the bytes as they come
//so when 1 byte typed == 1 byte read. Unless its new line then it is two.
private const int _DATASIZE = 1024;
public ConnectionServer()
{
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
_hostName = Dns.GetHostName();
_recievedData = new byte[_DATASIZE];
_incomingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endPoint = new IPEndPoint(localAddr, _LISTENINGPORT);
_incomingSocket.Bind(endPoint);
_incomingSocket.Listen(10);
}
~ConnectionServer()
{
}
public void StartListening()
{
_incomingSocket.BeginAccept(new AsyncCallback(OnAccept), _incomingSocket);
}
private void OnAccept(IAsyncResult result)
{
UserConnection connectionInfo = new UserConnection();
Socket acceptedSocket = (Socket)result.AsyncState;
connectionInfo.userSocket = acceptedSocket.EndAccept(result);
connectionInfo.messageBuffer = new byte[_DATASIZE];
//Begin acynch communication with target socket
connectionInfo.userSocket.BeginReceive(connectionInfo.messageBuffer, 0, _DATASIZE, SocketFlags.None,
new AsyncCallback(OnReceiveMessage), connectionInfo);
//reset the listnening socket to start accepting
_incomingSocket.BeginAccept(new AsyncCallback(OnAccept), result.AsyncState);
}
private void OnReceiveMessage(IAsyncResult result)
{
UserConnection connectionInfo = (UserConnection)result.AsyncState;
int bytesRead = connectionInfo.userSocket.EndReceive(result);
if (connectionInfo.messageBuffer[0] != 13 && connectionInfo.messageBuffer[1] != 10)
//ascii for newline and line feed
//todo dress this up
{
if (string.IsNullOrEmpty(connectionInfo.message))
{
connectionInfo.message = ASCIIEncoding.ASCII.GetString(connectionInfo.messageBuffer);
}
else
{
connectionInfo.message += ASCIIEncoding.ASCII.GetString(connectionInfo.messageBuffer);
}
}
else
{
connectionInfo.userSocket.Send(ASCIIEncoding.ASCII.GetBytes(connectionInfo.message), SocketFlags.None);
connectionInfo.userSocket.Send(connectionInfo.messageBuffer, SocketFlags.None);
connectionInfo.message = string.Empty;
connectionInfo.messageBuffer = new byte[_DATASIZE];
}
{
public class UserConnection
{
public Socket userSocket { get; set; }
public Byte[] messageBuffer { get; set; }
public string message { get; set; }
}
}
You seem to have several issues:
You have an asynchronous method called WaitForData. That's very confusing, as methods with the word Wait in their names generally block the currently executing thread until something happens (or, optionally, a timeout expires). This does the exact opposite. Are you intending for this to be a synchronous or asynchronous operation?
There's also no need to instantiate the Decoder object, nor do you need the char array for (it seems) anything; just call System.Text.Encoding.UTF8.GetString(socketData.DataBuffer, 0, iRx).
You also don't appear to be doing anything with lines...which is why it doesn't do anything with lines.
Your approach with using a StringBuilder is what I would do. I would add a StringBuilder to the SocketData class and call it Builder. As you capture string data, do something like this:
string[] data = System.Text.Encoding.UTF8.GetString(
socketData.DataBuffer, 0, iRx).Split(Environment.NewLine);
socketData.Builder.Append(data[0]);
for(int i = 1; i < data.Length; i++)
{
// the socketData.Builder variable now contains a single line, so do
// something with it here, like raise an event
OnLineReceived(builder.ToString());
socketData.Builder = new StringBuilder(data[i]);
}
The one caveat here is that UTF8 is a multi-byte encoding, meaning that you could potentially grab a chunk of data that cuts off mid-character. It's generally a better idea to do this sort of preprocessing on the other side of the communication, then send the data in an appropriately length-prefixed format.