Using the following code:
HttpListener listener = new HttpListener();
//listener.Prefixes.Add("http://*:80/");
listener.Prefixes.Add("http://*:8080/");
listener.Prefixes.Add("http://*:8081/");
listener.Prefixes.Add("http://*:8082/");
listener.Start();
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
The program hangs on the GetContext(); despite loading http (not https) pages in IE and Firefox.
When I uncomment the first line I get the error:
Failed to listen on prefix 'http://*:80/' because it conflicts with an
existing registration on the machine.
So how do I listen to a browser's requests?
#L.B I want to write a "proxy"
Don't reinvent the wheel and just use the FiddlerCore
public class HttpProxy : IDisposable
{
public HttpProxy()
{
Fiddler.FiddlerApplication.BeforeRequest += FiddlerApplication_BeforeRequest;
Fiddler.FiddlerApplication.Startup(8764, true, true);
}
void FiddlerApplication_BeforeRequest(Fiddler.Session oSession)
{
Console.WriteLine(String.Format("REQ: {0}", oSession.url));
}
public void Dispose()
{
Fiddler.FiddlerApplication.Shutdown();
}
}
EDIT
You can start with this rectangular wheel :)
void SniffPort80()
{
byte[] input = new byte[] { 1 };
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
socket.Bind(new IPEndPoint(IPAddress.Broadcast, 80));
socket.IOControl(IOControlCode.ReceiveAll, input, null);
byte[] buffer = new byte[0x10000];
Task.Factory.StartNew(() =>
{
while (true)
{
int len = socket.Receive(buffer);
if (len <= 40) continue; //Poor man's check for TCP payload
string bin = Encoding.UTF8.GetString(buffer, 0, len); //Don't trust to this line. Encoding may be different :) even it can contain binary data like images, videos etc.
Console.WriteLine(bin);
}
});
}
this port is probably being used...
run netstat -ano on the command line, youll see list of the ports that are being used and the their process ids.
I dont know, why the GetContext(); hangs, because there is too less information about what happens with the listerner variable, but the problem with port 80 usually is caused by Skype, because it uses port 80 by default. To fix that, open Skype's preferences, go to advanced->connection and uncheck "Use Port 80 and 443 as an alternative for incoming Connections".
I would consider looking into this package http://www.nuget.org/packages/Microsoft.AspNet.WebApi.OwinSelfHost/
It uses HttpListener under the covers and with the WebApi HttpMessageHandler it is very easy to create a proxy.
It hangs because GetContext() is waiting for a request to be received, as said in its documentation:
This method blocks while waiting for an incoming request. If you want incoming requests to be processed asynchronously (on separate threads) so that your application does not block, use the BeginGetContext method.
For more info see: https://msdn.microsoft.com/en-us/library/system.net.httplistener.getcontext(v=vs.110).aspx
Using Asynchronous model tends to be complex, another alternative is running all that code in a different Thread but it depends on your goals.
Replace * in your prefixes with +
listener.Prefixes.Add("http://+:8080/");
Related
There are multiple posts that describe the performance benefit of keeping a TCP connection open, instead of closing and opening each time you need to read or write. For example:
Best practice: Keep TCP/IP connection open or close it after each transfer?
I'm communicating with an RPC based device that takes json commands. The example I have from the device vendor opens and closes a connection each time they send a command. This is what I currently do via TcpClient in a using statement, but I'd like to see if there's anyway I could improve upon what I've already done. In fact, I had attempted this when starting the project, but couldn't figure out how to do so, so closed each time out of frustration and necessity. My latest experiment using sockets because all posts indicate doing so as a necessity for lower level control:
public class Connection
{
private Socket tcpSocket = null;
public string IpAddress = "192.168.0.30";
public int Port = 50002;
public Connection(string ipAddress, int port)
{
this.IpAddress = ipAddress;
this.Port = port;
}
public void Connect()
{
DnsEndPoint ipe = new DnsEndPoint(this.IpAddress, this.Port);
Socket tempSocket =
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
tempSocket.Connect(ipe);
if (tempSocket.Connected)
{
this.tcpSocket = tempSocket;
this.tcpSocket.NoDelay = true;
this.tcpSocket.
//this.tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive,true);
Console.WriteLine("Successfully connected.");
}
else
{
Console.WriteLine("Error.");
}
}
public void Disconnect()
{
this.tcpSocket.Disconnect(true);
this.tcpSocket.Dispose();
Console.WriteLine("Successfuly closed.");
}
public string SendCommand()
{
string path = #"C:\Users\me\Desktop\request.json";
string request = File.ReadAllText(path);
Byte[] bytesSent = Encoding.UTF8.GetBytes(request);
this.tcpSocket.Send(bytesSent);
this.tcpSocket.Shutdown(SocketShutdown.Send);
var respBytes = ReceiveAll();
string s = System.Text.Encoding.UTF8.GetString(respBytes, 0, respBytes.Length);
return s;
}
public byte[] ReceiveAll()
{
var buffer = new List<byte>();
var currByte = new Byte[1];
var byteCounter = this.tcpSocket.Receive(currByte, currByte.Length, SocketFlags.None);
while (this.tcpSocket.Available > 0)
{
currByte = new Byte[1];
byteCounter = this.tcpSocket.Receive(currByte, currByte.Length, SocketFlags.None);
if (byteCounter.Equals(1))
{
buffer.Add(currByte[0]);
}
}
return buffer.ToArray();
}
}
Console app:
static void Main(string[] args)
{
Connection s = new Connection();
s.Connect();
Console.WriteLine(s.SendCommand());
Console.WriteLine(s.SendCommand());
Thread.Sleep(5000);
s.Disconnect();
Console.ReadKey();
}
This approach works once. The first time I call send command. It doesn't the second time (throws an exception), because I call socket.Shutdown() on Send in my SendCommand(). I do so because of this post:
TCPClient not receiving data
However, there doesn't seem to be a way to re-enable the ability to Send after calling Shutdown(). So now I just don't know if it's even possible to keep a tcp connection open if you have to both read and write. Moreover, I can't really find a useful example online. Does anyone know how to do so in .NET? Is this even possible?
TCP/IP is a streaming protocol. To pass messages with it you need a “framing protocol” so peers can determine when a message is finished.
One simple way to signal the end of a message is to close the socket when you’ve sent the last byte. But this prevents socket reuse. See the evolution of HTTP for an example of this.
If this is what your device does, there’s no way to reuse a socket.
If it is possible to keep the connection open for more messages depends on the application protocol. There is no way to enforce this if the protocol does not supports it. Thus, ask the vendor or look into the protocol specification (if it exists) for information if and how this is supported.
However, there doesn't seem to be a way to re-enable the ability to Send after calling Shutdown().
There is no way. TCP write shutdown means that one does not want to send any more information. It is impossible to take this back. If the protocol supports multiple message exchanges then it needs to have a different way to detect the end of a message than calling Shutdown.
I need a simple connectionless ClientServer application to exchange a bunch of information with a wireless device.
Before to talk about problems I'll show what I've till now:
Environment: C#, WPF, .NetFramework 4.7.1, Visual Studio 2017
Logical Organization: UDP -> UDPServer -> UDPSpecServer; UDP -> UDPClient
Code:
// Server App ctor
endPoint = new IPEndPoint(IPAddress.Any, 0);
socket.Bind(new IPEndPoint(ip, connPort));
which should basically means that I accepts packets from everyone at specified ip (localhost) / port (1984). At some point in the code I have:
public void StartReceivingConnectionless()
{
IAsyncResult result;
try
{
result = socket.BeginReceiveFrom(buffer, 0, MAX_UDP_PACKET_DIMENSION, SocketFlags.None, ref endPoint, new AsyncCallback(MessageReceived), buffer);
}
catch(Exception exception)
{
System.Diagnostics.Debug.Print(exception.Message);
}
}
which should basically means that my server object starts to wait for incoming data, while the MessageReceived method:
public virtual void MessageReceived(IAsyncResult asynchronousResult)
{
int bytes = socket.EndReceiveFrom(asynchronousResult, ref endPoint);
StartReceivingConnectionless();
}
And finally, for Serverside:
public void StopReceivingConnectionless()
{
socket.Close();
}
While on client:
// Clientapp ctor
client = new FST.Lib.NetworkCommunication.UDPClient("localhost", "0");
// On-click somewhere
client.SendConnectionless(Encoding.ASCII.GetBytes("Hello world"), new System.Net.IPEndPoint(System.Net.IPAddress.Parse("127.0.0.1"), 1984));
1) I still have to undesterstand why/how, but sometimes the app works, sometimes no: why?
2) The stopreceveing method is placed in the "Window_closing" event of main window, no matter what, it generates a disposed object exception because it generate a message which is recevied. Where should I put it?
For part 1) - it seems that my Virtual Machines was the problem. I replicated the same library at home and everything went fine. In this case, I solved using UdpClient with less or more the same architecture.
Part 2) is still a partial puzzle: I was missing the call to socket.Shutdown(); and then socket.Close(); as pointed out here but this call generates a message received from the same socket causing the crash.
I'm accepting this answer, but more detailed ones are still welcome.
I am trying to send commands to the server , like for example requesting the server to send back the list of files in it's directory. The problem is that when I send the "list" command to the server, I have to send it twice in order for the server to send back the list of files to the client. I am sure that the server receives the command in both times as on the server side I print the result that is supposed to be sent to the client on the console and it appears both times.
I am using C# and TCPListeners to listen for incoming responses or commands, and TCPClient to send responses or commands between the server and the client.
The client code
private TcpListener tcpListener = new TcpListener(9090);
private void button3_Click(object sender, EventArgs e)
{
Byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes("list");
try
{
TcpClient clientSocket = new TcpClient(serverIPFinal, 8080);
if (clientSocket.Connected)
{
NetworkStream networkStream = clientSocket.GetStream();
networkStream.Write(bytesToSend, 0, bytesToSend.Length);
// networkStream.Close();
// clientSocket.Close();
thdListener = new Thread(new ThreadStart(listenerThreadList));
thdListener.Start();
}
}
catch
{
isConnectedLbl.Text = "Server not running";
}
}
//Listener Thread to receive list of files.
public void listenerThreadList()
{
tcpListener.Start();
while (true)
{
handlerSocket = tcpListener.AcceptSocket();
if (handlerSocket.Connected)
{
Control.CheckForIllegalCrossThreadCalls = false;
lock (this)
{
if (handlerSocket != null)
{
nSockets.Add(handlerSocket);
}
}
ThreadStart thdstHandler = new
ThreadStart(handlerThreadList);
Thread thdHandler = new Thread(thdstHandler);
thdHandler.Start();
}
}
}
//Handler Thread to receive list of files.
public void handlerThreadList()
{
Socket handlerSocketList = (Socket)nSockets[nSockets.Count - 1];
NetworkStream networkStreams = new NetworkStream(handlerSocketList);
int requestRead = 0;
string dataReceived;
byte[] buffer = new byte[1024];
//int iRx = soc.Receive(buffer);
requestRead = networkStreams.Read(buffer, 0, 1024);
char[] chars = new char[requestRead];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(buffer, 0, requestRead, chars, 0);
dataReceived = new System.String(chars);
Console.WriteLine(dataReceived);
MessageBox.Show(dataReceived);
//tcpListener.Stop();
thdListener.Abort();
}
The Server code:
TcpListener tcpListener = new TcpListener(8080);
public void listenerThreadCommands()
{
tcpListener.Start();
while (true)
{
handlerSocket = tcpListener.AcceptSocket();
if (handlerSocket.Connected)
{
Control.CheckForIllegalCrossThreadCalls = false;
connections.Items.Add(
handlerSocket.RemoteEndPoint.ToString() + " connected.");
// clientIP = handlerSocket.RemoteEndPoint.ToString();
lock (this)
{
nSockets.Add(handlerSocket);
}
ThreadStart thdstHandler = new
ThreadStart(handlerThreadCommands);
Thread thdHandler = new Thread(thdstHandler);
thdHandler.Start();
//tcpListener.Stop();
//handlerSocket.Close();
}
}
}
//Handler Thread to receive commands
public void handlerThreadCommands()
{
Socket handlerSocketCommands = (Socket)nSockets[nSockets.Count - 1];
NetworkStream networkStream = new NetworkStream(handlerSocketCommands);
int requestRead = 0;
string dataReceived;
byte[] buffer = new byte[1024];
requestRead = networkStream.Read(buffer, 0, 1024);
char[] chars = new char[requestRead];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(buffer, 0, requestRead, chars, 0);
dataReceived = new System.String(chars);
//connections.Items.Add(dataReceived);
if (dataReceived.Equals("list"))
{
localDate = DateTime.Now;
Files = Directory.GetFiles(System.IO.Directory.GetCurrentDirectory())
.Select(Path.GetFileName)
.ToArray();
String FilesString = "";
for (int i = 0; i < Files.Length; i++)
{
FilesString += Files[i] + "\n";
}
String clientIP = handlerSocketCommands.RemoteEndPoint.ToString();
int index = clientIP.IndexOf(":");
clientIP = clientIP.Substring(0, index);
WriteLogFile(logFilePath, clientIP, localDate.ToString(), " ", "list");
Console.WriteLine(clientIP);
Console.WriteLine(FilesString);
Byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(FilesString);
try
{
WriteLogFile(logFilePath, clientIP, localDate.ToString(), " ", "list-response");
TcpClient clientSocket = new TcpClient(clientIP, 9090);
if (clientSocket.Connected)
{
NetworkStream networkStreamS = clientSocket.GetStream();
networkStreamS.Write(bytesToSend, 0, bytesToSend.Length);
networkStreamS.Close();
clientSocket.Close();
networkStream.Close();
//tcpListener.Stop();
// handlerSocketAuthenticate.Close();
}
}
catch
{
Console.WriteLine("Cant send");
}
}
else if (dataReceived.Equals("downloadfile"))
{
// handlerSocketAuthenticate.Close();
// tcpListener.Stop();
networkStream.Close();
thdListenerDownload = new Thread(new ThreadStart(listenerThreadDownloading));
thdListenerDownload.Start();
}
else
{
String clientIP1 = handlerSocketCommands.RemoteEndPoint.ToString();
int index = clientIP1.IndexOf(":");
clientIP1 = clientIP1.Substring(0, index);
// handlerSocketAuthenticate.Close();
CommandExecutor(dataReceived, clientIP1);
}
}
There are so many different things wrong with the code you posted, it's hard to know where to start, and it's impossible to have confidence that in the context of a Stack Overflow, one could sufficiently address all of the deficiencies. That said, in the interest of helping, it seems worth a try:
Sockets are bi-directional. There is no need for the client to use TcpListener at all. (By convention, the "server" is the endpoint that "listens" for new connections, and the "client" is the endpoint that initiates new connections, by connecting to a listening server.)You should just make a single connection from client to server, and then use that socket both for sending to and receiving from the server.
You are setting the CheckForIllegalCrossThreadCalls property to false. This is evil. The exceptions that occur are there to help you. Setting that property to false disables the exceptions, but does nothing to prevent the problems that the exceptions are designed to warn you about.You should use some mechanism to make sure that when you access UI objects, you do so only in the thread that owns those objects. The most primitive approach to this is to use Control.Invoke(). In modern C#, you are better off using async/await. With TcpClient, this is easy: you already are using GetStream() to get the NetworkStream object that represents the socket, so just use the asynchronous methods on that object, such as ReadAsync(), or if you wrap the stream in a StreamWriter and StreamReader, use the asynchronous methods on that object, such as ReadLineAsync().
You are checking the Connected property of the TcpClient object. This is pointless. When the Connect() method returns, you are connected. If you weren't, an exception would have been thrown.
You are not sufficiently synchronizing access to your nSockets object. In particular, you use its indexer in the handlerThreadList() method. This is safe when using the object concurrently only if you have guaranteed that no other thread is modifying the object, which is not the case in your code.
You are writing to the stream using ASCII encoding, but reading using UTF8 encoding. In practice, this is not really a problem, because ASCII includes only the code points 0-127, and those map exactly to the same character code points in UTF8. But it's really bad form. Pick one encoding, stick with it.
You are accepting using AcceptSocket(), but then just wrapping that in a NetworkStream anyway. Why not just use AcceptTcpClient() and call GetStream() on that? Both Socket and TcpClient are fine APIs, but it's a bit weird to mix and match in the same program, and will likely lead to some confusion later on, trying to keep straight which you're using where and why.
Your code assumes that the handlerThreadCommands() method will always be called in exactly the same order in which connections are accepted. That is, you retrieve the current socket with nSockets[nSockets.Count - 1]. But, due to the way Windows thread scheduling works, it is entirely possible that two or more connections could be accepted before any one of the threads meant to handle the connection is started, with the result that only the most recent connection is handled, and it is handled by those multiple threads.
You are assuming that command strings will be received as complete units. But this isn't how TCP works. TCP guarantees only that if you receive a byte, it will be in order relative to all the bytes sent before it. But you can receive any number of bytes. In particular, you can receive just a single byte, or you can receive multiple commands concatenated with each other, or you can receive half a command string, then the other half later, or the second half of one command and the first half of the next, etc. In practice, these problems don't show up in early testing because the server isn't operating under load, but later on they very well may be. And the code needs to be designed from the outset to work properly under these conditions; trying to patch bad code later is much more difficult.
I can't say that's the above are the only things wrong with the code, but they are most glaring, and in any case I think the above is sufficient food for thought for you at the moment.
Bottom line: you really should spend more time looking at good networking examples, and really getting to understand how they work and why they are written the way they do. You'll need to develop a good mental model for yourself of how the TCP protocol works, and make sure you are being very careful to follow the rules.
One resource I recommend highly is The Winsock Programmer's FAQ. It was written long ago, for a pre-.NET audience, but most of the information contained within is still very much relevant when using the higher-level networking APIs.
Alternatively, don't try to write low-level networking code yourself. There are a number of higher-level APIs that use various serialization techniques to encode whole objects and handle all of the lower-level network transport mechanics for you, allowing you to concentrate on the value-added features in your own program, instead of trying to reinvent the wheel.
Alright, so I'm pretty new to C# and I'm definitely pretty new to graphical programming. I'm using Visual Studio 2015 and writing my application in C#.
I have this hunk of code that I've been toying around with for a while. Essentially my program will send the HELLO, to the server, but the server isn't sending HELLO back. I have no firewall in the middle of client and server right now, but the process is getting hung waiting for the reply back. I honestly don't even want to do it this way, I want the listener to always run in the background while the user does other stuff so that my program functions, well normal. So I come to you oh great Stackoverflow... because I am definitely doing it wrong! Could someone please point me the right direction?
Current Code:
private void button1_Click(object sender, EventArgs e)
{
byte[] data = new byte[512];
byte[] result;
SHA512 shaM = new SHA512Managed();
result = shaM.ComputeHash(Encoding.UTF8.GetBytes(this.password.Text));
var hash = BitConverter.ToString(result).Replace("-", "");
this.send_message("127.0.0.1", 10545, 10545, "HELLO");
this.send_message("127.0.0.1", 10545, 10545, "AUTH:" + this.login.Text + ":" + hash);
//ListenForData.Start();
}
private void send_message(string server, int localPort, int remotePort, string message)
{
label4.Text = "Listening on port:" + localPort;
IPEndPoint lep = new IPEndPoint(IPAddress.Any, localPort);
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPAddress loginServer = IPAddress.Parse(server.ToString());
byte[] sendbuf = Encoding.ASCII.GetBytes(message);
IPEndPoint ep = new IPEndPoint(loginServer, remotePort);
s.SendTo(sendbuf, ep);
try
{
UdpClient udpClient = new UdpClient(lep);
byte[] bytes = udpClient.Receive(ref lep);
label4.Text = ep.ToString();
} catch ( Exception ex )
{
Console.WriteLine(ex.ToString());
}
}
UDPATE:
private static void UDPListener(object obj)
{
Task.Run(async () =>
{
using (var udpClient = new UdpClient(10545))
{
string rMessage = "";
string[] rArgs = new string[0];
while (true)
{
//IPEndPoint object will allow us to read datagrams sent from any source.
var receivedResults = await udpClient.ReceiveAsync();
rMessage += Encoding.ASCII.GetString(receivedResults.Buffer);
rArgs = rMessage.Split(new char[] { ':' });
if( rArgs[0] == "HELLO")
{
Console.Write("Received HELLO from server.");
byte[] data = new byte[512];
byte[] result;
SHA512 shaM = new SHA512Managed();
result = shaM.ComputeHash(Encoding.UTF8.GetBytes(obj.password.Text));
var hash = BitConverter.ToString(result).Replace("-", "");
send_message("127.0.0.1", 10545, 10545, "AUTH:" + obj.login.Text + ":" + hash);
}
}
}
});
}
You can safely ignore the advice from Blindy. The UdpClient.ReceiveAsync() method is specifically designed around the Task paradigm, and is much more efficient than dedicating a thread to receiving data. Because the Task is awaitable, it's also easier to integrate the ReceiveAsync() approach with a GUI program (e.g. Winforms or WPF). That said, for all that to work, you want to execute the ReceiveAsync() call in the UI thread, rather than in another Task.
Unfortunately, lacking a good, minimal, complete code example that clearly illustrates your question, it's not possible to say for sure how that would look. But it most likely would involve making your UDPListener() method an async method and calling it from the UI thread. Also, since you say the method can't access non-static members, the obvious solution to that is to make the method itself non-static. E.g.:
private static async Task UDPListener(object obj)
{
using (var udpClient = new UdpClient(10545))
{
string rMessage = "";
string[] rArgs = new string[0];
while (true)
{
//IPEndPoint object will allow us to read datagrams sent from any source.
var receivedResults = await udpClient.ReceiveAsync();
rMessage += Encoding.ASCII.GetString(receivedResults.Buffer);
rArgs = rMessage.Split(new char[] { ':' });
if( rArgs[0] == "HELLO")
{
Console.Write("Received HELLO from server.");
byte[] data = new byte[512];
byte[] result;
SHA512 shaM = new SHA512Managed();
result = shaM.ComputeHash(Encoding.UTF8.GetBytes(obj.password.Text));
var hash = BitConverter.ToString(result).Replace("-", "");
send_message("127.0.0.1", 10545, 10545, "AUTH:" + obj.login.Text + ":" + hash);
}
}
}
}
It is not clear from your updated question whether you have fixed your failed to receive a reply yet. But if you have not changed your send_message() method, it has some obvious problems, especially in the context of creating a separate UdpClient instance to receive datagrams.
The most obvious issue is that you do not create the UdpClient instance which you're expecting to use to receive the response until after you have sent the message. This is wrong for two reasons:
It's entirely possible that the remote endpoint will send the response before you've created the socket. If that happens, the datagram will just be dropped.
More importantly, the usual design for a server is to send a response to the remote endpoint that sent it the message in the first place. I.e. in this case that would be the socket s. Even if you did create the udpClient object before calling s.SendTo(), the reply would actually be sent to the s socket, not the udpClient.Client socket.
Again, without a good code example it's not possible to know what your server actually does. But whether it behaves like a normal server, or is some non-standard implementation, the code you've posted cannot reliably receive a response from the server.
You should fix your design so that your client creates only a single socket (e.g. an initial UdpClient instance). You would prepare for communication by calling ReceiveAsync(), and only once you've done that, thus ensuring you're ready to receive a response, then you can send data.
Make sure that you create only this single object.
Note also that client-side implementations typically do not bind to a specific port. Instead, you let the OS assign a port. Only the server needs to bind to a specific port, so that inbound requests from unknown endpoints can know to what port to send their request. The server's receive operation will include the port number of the client, so the server will know to what port to send its response, without the client having selected any special port number.
Finally, I strongly recommend you study existing socket programming tutorials, and especially the Winsock Programmer's FAQ. None of the material there is directly applicable to the .NET socket API, but most of the issues you will have trouble with are exactly the kinds of things documented there. It is not possible to use the .NET socket API without also having a good basic comprehension of socket programming generally.
If the above does not get you headed in the right direction, please make sure that any future questions you ask include a good code example, as described in the link I provided above. Note that for any networking question, a complete code example includes both the client and server. It is not possible for anyone to fully understand your question, never mind to test and fix your code example, without implementations of both.
Noob question.
So i try this code to call http server for resource many times on same socket:
public void TryReuseSameSocket(){
var addr = Dns.GetHostAddresses("stackoverflow.com");
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
try
{
// i thought that i must to use this option
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
socket.Connect(addr, 80);
var bigbuff = new byte[10000];
for (var i = 0; i < 20; i++)
{
//now i try to call some URL many times without socket closing
var buff = Encoding.ASCII.GetBytes("GET /help/badges HTTP/1.1\r\nHost: stackoverflow.com\r\nConnection: Keep-Alive\r\n\r\n");
socket.Send(buff);
var reciveCount = 0;
var totalCount = 0;
while (true)
{
reciveCount = socket.Receive(bigbuff);
totalCount += reciveCount;
if (0 == reciveCount) break;
}
//only first call will proceed, all others will not have a result
Assert.AreNotEqual(0,totalCount);
}
}
finally
{
socket.Dispose();
}
}
But only first call proceed, all others return no data at all in recieve.
How to reuse socket with HTTP server correctly.
You are reading the stream until it is closed by the remote side. It makes sense that after that point you won't get any data.
You need to make the server keep the connection alive. This is done by setting a keep alive HTTP header. You seem to be doing that.
With HTTP keep alive the server will set the Content-Length header to tell you how many bytes to read. Read exactly that many bytes. After that, send the next request.
TCP keep alives (SocketOptionName.KeepAlive) have nothing to do with HTTP keep alives. Remove that code.
In addition the mistakes in my other answer you are not instructing the server to keep the connection alive. The proper header value is keep-alive. That's why the server closes the connection.
Thx. Now I have found solution:
1) Don't forget check socket.Available to decide is stream read to end, checking of stream.Read(...) == 0 or Recive(...)==0 is not real thing
2) Don't trust Internet - some servers return Keep-Alive but it's not true - connection is opened, but all subrequests fails
3) Snippet in question is working if reading logic will looks like
while (socket.Available != 0)
{
socket.Receive(bigbuff);
}
4) It's not 100% always good (u need check some other socket things to controll reading), but on sites with real Keep-Alive support it works well.