I have a set of requirements for a client/server application as listed below:
1) Program sends a statuscheck message to a host which is listening on a predefined UDP port. This message is sent on a source port number given by the OS.
2) The program needs to listen on the source port number initiated in step 1 to receive the response from the remote host. The program therefore must listen on thousands of port at the same time.
3) This process needs to be done for thousands of hosts per minute
Below I've created a sample example that sends a large number of requests to an Echo Server to mimic this behaviour. The problem that I'm facing is that although I close each socket after receiving data from the remote host, after about 16,000 requests an exception is thrown saying system lacked sufficient buffer space or queue was full.
What would be a way to achieve such requirements?
public void SendChecks()
{
IPEndPoint ip = new IPEndPoint(IPAddress.Parse("1.1.1.1"), 7);
for (int i = 0; i < 200000; i++)
{
Socket _UdpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
stateobject so = new stateobject(_UdpSocket);
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;
_UdpSocket.Bind(tempRemoteEP);
string welcome = "Hello";
byte[] data = new byte[5];
data = Encoding.ASCII.GetBytes(welcome);
_UdpSocket.BeginSendTo(data, 0, data.Length, SocketFlags.None, ip, new AsyncCallback(OnSend), _UdpSocket);
//Start listening to the message send by the user
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
_UdpSocket.BeginReceiveFrom(so.buffer, 0, so.buffer.Length, SocketFlags.None, ref newClientEP, new AsyncCallback(DoReceiveFrom), so);
}
}
private void DoReceiveFrom(IAsyncResult ar)
{
try
{
stateobject so = (stateobject)ar.AsyncState;
Socket s = so.sock;
// Creates a temporary EndPoint to pass to EndReceiveFrom.
IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)sender;
int read = s.EndReceiveFrom(ar, ref tempRemoteEP);
so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0, read))
//All 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();
s.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private void OnSend(IAsyncResult ira)
{
Socket s = (Socket)ira.AsyncState;
Console.WriteLine("Sent Data To Sever on port {0}",((IPEndPoint)s.LocalEndPoint).Port);
s.EndSend(ira);
}
}
I think the best way to go in terms of performance and simplicity would be to simply use a single port number anywhere between:
1025 - 65553
Then when listening for thousands of messages from other peers they also send to a predefined known port number and you can process them asynchronously.
To listen to a known port number, in this case 60000:
mySocket.Bind(new IPEndPoint(IPAddress.Any, 60000));
Also do not close the socket after each operation! Keep it open and re-use it.
Properly written it would be walk in the park for .Net and the OS to handle your requirements.
Related
I am creating and sending a message on server socket and not getting any response in return , I would like to know what could be possible reasons for the same. Below is my sample code for client socket
static void Main()
{
int _DCPport = 9090;
IPHostEntry ipHostInfo = Dns.GetHostEntry("HostName");
Console.WriteLine("Got the IP Host Info" , ipHostInfo.ToString ());
IPAddress ipAddress = ipHostInfo.AddressList[0];
Console.WriteLine("Got the IP hadress Info");
//End point of the host where the socket will be connected
IPEndPoint remoteDAPEndPoint = new IPEndPoint(ipAddress, _DCPport);
// Create a clientSocket that connects to host
Socket clientDAP = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientDAP.Connect(remoteDAPEndPoint);
Console.WriteLine("Client socket connected to remote end point");
#region Input variables
string requestString = "Hello";
string responseString = "";
byte[] requestByte = System.Text.Encoding.UTF8.GetBytes(requestString);
byte[] responseByte = new byte[1024];
StringBuilder messageBuilder = new StringBuilder();
int byteResult = 0;
#endregion
try
{
//sending the string as bytes over the socket
Console.WriteLine("Sending the input " + requestString);
clientDAP.Send(requestByte);
}
catch (SocketException eX)
{
Console.WriteLine(eX.Message);
}
try
{
//recieving the response bytes
byteResult = clientDAP.Receive(responseByte, clientDAP.Available, SocketFlags.None);
Console.WriteLine("Recieved {0} bytes as response from remote end point" , byteResult);
if (clientDAP.Connected)
{
responseString = Encoding.UTF8.GetString(responseByte);
messageBuilder.Append(responseString);
}
}
catch (SocketException eX)
{
Console.WriteLine(eX.Message);
}
clientDAP.Shutdown(SocketShutdown.Both);
clientDAP.Disconnect(true);
string sResult = messageBuilder.ToString();
Console.WriteLine(sResult);
}
I am not getting any bytes when i call socket.Recieve() , Any reasons why such is happening ?
From Socket.Send:
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
Which means that the data may not have even arrived at the server yet when your call to Send has completed. Let alone that the server has had time to process your data and return a response.
And yet your code charges straight ahead to try to receive the server's response. This isn't unreasonable, in and of itself. Your call to Receive would have blocked until it had received some data if you had specified the size of your buffer, rather than using Available, which will happily return 0 if there's no data yet available. So your Receive call is asking for up to 0 bytes and that's how many you're being given.
So change this line
byteResult = clientDAP.Receive(responseByte, 1024, SocketFlags.None);
(Or better yet, introduce a constant and/or use responseByte.Length or switch to an overload where you don't explicitly specify how many bytes you want)
I'm fairly new with UDP sockets with most of my socket experience being with TCP.
I'm using C# to create a UDP socket, bind it and then continually listen for packets from an IP. However, when the client sends a packet, the socket throws an exception: "An existing connection was forcibly closed by the remote host".
This is how I create the UDP socket:
Socket UDPSocket = new Socket(SocketType.Dgram, ProtocolType.Udp); //Create UDP socket
UDPSocket.Bind(new IPEndPoint(IPAddress.Any, 1338)); //Bind port 1338 from any IP
After creating the socket, I asynchronously receive data:
UDPSocket.BeginReceiveFrom(newClient.bufferu, 0, AbstractServerClient.BUFFER_SIZE, 0, ref newClient.remoteEndpoint,new AsyncCallback(ReceiveCallbackUDP), newClient);
newClient.remoteEndpoint an endpoint from a TCP socket, so the UDP is effectively receiving packets from the same IP that the TCP is connected to. Assume the rest of the paramaters are valid (They work fine on TCP).
The ReceiveCallbackUDP method looks like this:
public void ReceiveCallbackUDP(IAsyncResult ar)
{
AbstractServerClient client = (AbstractServerClient)ar.AsyncState;
int bytesRead = UDPSocket.EndReceive(ar);
if (bytesRead > 0)
{
Bitstream data = new Bitstream(client.bufferu, 12);
int bytesProcessed = 12;
while (bytesProcessed < bytesRead)
{
ushort messageLength = data.ReadUInt16(); //Read the message length
byte messageID = data.ReadByte(); //Read the message ID
// Logger.Log("Received message ID: " + messageID.ToString() + " (Size: " + messageLength + " bytes)");
bytesProcessed += messageLength + sizeof(UInt16);
client.GetPacketHandler(messageID).HandlePacket(data); //Process the message
bytesProcessed += 12;
data.Seek(bytesProcessed); //Seek to the next message
}
}
UDPSocket.BeginReceiveFrom(client.bufferu, 0, AbstractServerClient.BUFFER_SIZE, 0, ref client.remoteEndpoint, new AsyncCallback(ReceiveCallbackUDP), client);
}
This function calls EndReceive() on the socket to read the data, and processes it, and called BeginReceiveFrom to wait for more data (asynchronously). The exception is thrown on the line int bytesRead = UDPSocket.EndReceive(ar);
Why is this happening? To my understanding, UDP is a connectionless socket so the exception "An existing connection was closed" doesn't make sense. Am I doing something wrong here?
In BeginReceiveFrom your remoteEP should indentify the socket that is listening, mostly the same endpoint that you have used for the Bind.
And in your callback method ReceiveCallbackUDP you should call EndReceiveFrom, as this is the corresponding Method to BeginReceiveFrom:
EndPoint tempRemoteEP = (EndPoint)client.remoteEndpoint;
int bytesRead = UDPSocket.EndReceiveFrom(ar, ref tempRemoteEP);
See the Example in http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.beginreceivefrom.aspx, where tempRemoteEP passed to BeginReceiveFrom is the same Endpoint as sender passed to Bind.
i have a GPS device that will be installed in many trucks.
i can configure the device to send data statement "gps data, device id" over gprs to IP and Port.
i'm using TcpListener class to read the data on the server side.
TcpListener server = null;
private void listen_data()
{
Int32 port = controller_port;
IPAddress localAddr = IPAddress.Parse(this_ip);
server = new TcpListener(localAddr, port);
server.Start();
Byte[] bytes = new Byte[256];
String data = null;
while (true)
{
Console.Write("Waiting for a connection...-- ");
TcpClient client = server.AcceptTcpClient();
Console.Write("Connected!");
data = null; int i;
NetworkStream stream = client.GetStream();
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
}
}
}
that method is listening to what is coming on server ip and port.
i want to know if i configured the devices to send to the server on the same port.am i able to listen to all the devices or the first device to connect will be the only one ?
is this method the the best way to read the coming data from the devices?
do i need to configure a different port for each device and create a new listen thread for each device port?
sometimes i'm facing exceptions "the request channel timed out while waiting for a reply"
many thanks in advance for your help.
In your code you are listening to the all devices but only after finish read all data from the first device so you are receiving "the request channel timed out while waiting for a reply".You should have a different threads each one handle a tcpClient.
so the code should be something like:
TcpListener server = null;
private void listen_data()
{
Int32 port = controller_port;
IPAddress localAddr = IPAddress.Parse(this_ip);
server = new TcpListener(localAddr, port);
server.Start();
while (true)
{
Console.Write("Waiting for a connection...-- ");
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("new client connected");
ThreadPool.QueueUserWorkItem(new WaitCallback(HandleClient), client);//or use Task if 4.0 or new Thread...
}
}
private void HandleClient(object tcpClient)
{
TcpClient client = (TcpClient)tcpClient;
Byte[] bytes = new Byte[256];
String data = null;
int i;
NetworkStream stream = client.GetStream();
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
}
Console.WriteLine(data);
}
1) Both. You should be able to listen for all devices, but you often cannot with your code because the listener thread is tied up waiting for the stream from a device that connected earlier.
2) Probably not. IIRC, NetworkStream.Read returns 0 when the connection is closed by the peer device. Is this your protocol - ie. the device connects, sends some data and disconnects? If so that will work, though slowly. Anyway, there is another problem. You should be concatenating the bytes received on your stream to data, not just replacing them - Read() my return multiple times for one communication, perhaps even with a single byte each time, (unlikely, but permitted with TCP streams). You could keep a count of bytes rx. so far and use the 'offset' parameter to do this.
3) You only need one listening thread, ie. the one that calls AcceptTcpClient(). This thread should not be making blocking calls to receive data from the socket returned by AcceptTcpClient(). Either create/allocate/depool/whatever a new client-server thread to run your Read() loop for each 'client' socket returned by AcceptTcpClient() or use asynchronous IO.
4) Your single listener/read thread will be non-responsive for new connections while it is waiting on the NetworkStream - other devices will be unable to connect. The listener should get back to AcceptTcpClient() quickly and not wait for slow networks/devices to send data.
Rgds,
Martin
I noticed that BT Home are sending back fake DNS results from their DNS servers and this allows sites to bypass the IP addresses i have blocked in the firewall so i was looking to create my own DNS relay/server.
So far i can receive request on UDP port 53 and send them off to the DNS server and get a valid byte[] stream result and i then send back to the browser using the remote client port the request was made on but the browser just sends the request back again.
I've tested the code from a socket and the results work OK but for some reason IE/FF simply will not except the results.
public void Listen()
{
receiveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp );
receiveEndPoint = new IPEndPoint(IPAddress.Any, receivePort); receiveSocket.Bind(receiveEndPoint);
receivePort = (receiveSocket.LocalEndPoint as IPEndPoint).Port;
receiveBuffer = new byte[BufferSize];
receiveAsyncResult = receiveSocket.BeginReceiveFrom(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, ref receiveEndPoint, new AsyncCallback(NetworkMessageReceivedCallback), receiveSocket);
}
public void NetworkMessageReceivedCallback(IAsyncResult asyncResult)
{
EndPoint remoteEndPoint = null;
byte[] bytes = null;
remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); //Will contain the clients port
int bytesRead = receiveSocket.EndReceiveFrom(asyncResult, ref remoteEndPoint);
bytes = new Byte[bytesRead];
Buffer.BlockCopy(receiveBuffer, 0, bytes, 0, bytesRead);
//string ip = "208.67.222.222";
string ip = "192.168.1.254";
IPAddress dnsServer = IPAddress.Parse(ip);
Response R = Resolver.Lookup(bytes, dnsServer);
receiveSocket.SendTo(R.Message , remoteEndPoint);//127.0.0.1
receiveSocket.Close();
Listen();
}
I never dealt with raw DNS from C# but it looks like you are trying to resolve the bytes you received from the client, instead of just relaying them to the DNS server.
The message you read off the UDP socket contains a DNS query, not just a host name. Take a look at the RFC 2929 for what goes in there.
You might be interested in this little but great DNS filter - adsuck - by Marco Peereboom (though it's for Unix, not Windows).
Also, shouldn't your try and listen to UDP and TCP. I think UDP is used mostly for authoritative DNS queries.
I have an embedded Ethernet interface (Lantronix XPort) that responds to a UDP broadcast with its identifying information.
I am able to multicast the "magic packet" and datagrams are received by the listener correctly, however I also need to find out what IP Address send that response datagram. If it were TCP, I would do socket.RemoteEndPoint, but that throws an exception when applied to a UDP socket.
public class Program
{
public static void Main(string[] args)
{
// magic packet
byte[] magicPacket = new byte[4] { 0, 0, 0, 0xf6 };
// set up listener for response
Socket sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// EDIT: Also, had to add this to for it to broadcast correctly
sendSocket.EnableBroadcast = true;
IPEndPoint listen_ep = new IPEndPoint(IPAddress.Any, 0);
sendSocket.Bind(listen_ep);
// set up broadcast message
EndPoint send_ep = new IPEndPoint(IPAddress.Parse("192.168.255.255"), 30718);
sendSocket.SendTo(magicPacket, magicPacket.Length, SocketFlags.None, send_ep);
DateTime dtStart = DateTime.Now;
while (true)
{
if (sendSocket.Available > 0)
{
byte[] data = new byte[2048];
// throws SocketException
//IPEndPoint ip = sendSocket.RemoteEndPoint as IPEndPoint;
sendSocket.Receive(data, SocketFlags.None);
if (data.Length > 4)
{
PrintDevice(data);
}
}
if (DateTime.Now > dtStart.AddSeconds(5))
{
break;
}
Console.WriteLine(".");
Thread.Sleep(100);
}
// wait for keypress to quit
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
Any thoughts? Is there a better strategy to reading the response datagrams that would let me ascertain the Remote IP Address?
EDIT:
As is typical, the minute I post on SO, a moment of clarity hits me.
Turns out I can just do this:
EndPoint remote_ep = new IPEndPoint(IPAddress.Any, 0);
// Use ReceiveFrom instead of Receieve
//sendSocket.Receive(data, SocketFlags.None);
sendSocket.ReceiveFrom(data, ref remote_ep);
And remote_ep now contains the remote endpoint information!
Take a look at ReceiveFrom instead of Receive, it will let you pass in a reference to an Endpoint.
What about Asynchronous socket?I didn't find any way to get the remote IP address. (Asynchronous method ReceiveFromAsync is my only option in wp8)
EndPoint remote_ep = new IPEndPoint(IPAddress.Any, 0); // Use ReceiveFrom instead of
sendSocket.Receive(data, SocketFlags.None);
sendSocket.ReceiveFrom(data, ref remote_ep);
i think it works for IP but it fails for port number
if you would notice
try chaging 0 to something else like 6530 4expl
it system will would generate it's random port number
Any ideas to why is it ?
P.S. Any ideas how can i change my user name here .... ?
FOUND IT : the abstract class only needed for representation of port it is in value not out
since there's no bind done before hand this operation ref EndPoint needed to represent the sender. Meaning that it is there to show senders port and IP not to specify from where to get the communication. And EndPoint instantiation is really just a formality seems like it is overriden by system with senders address anyway. I think it has to do somethign with the way UDP protocol works.
But all in all the ref EndPoint is there only shows where u got the packet from and only it does not specify where u want u'r commuicatino to be from.