Asynchronous Client Server Communication Issue - c#

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.

Related

Socket connected but doesn't have Remote EndPoint

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.

C# Multithreading Clients Duplicate Connection/Data Loss

I am having a weird problem where I am making a chat connection with TCPListeners and sockets. For some reason when a client "connects" to the server it will show they have connected twice. Also what is weird once all of the users have connected I will send out a message to all of them. They will respond back with it acknowledged and state that their chat has started.
Things I am noticing with how i have it setup:
It appears according to the log that the user "connects" twice the second "connect occurs once it hits the white(true) loop.
When they send over the acknowledgement back to the server not all of the data is getting sent over. If I do a thread sleep on the client it does appear to start working but it is still inconsistent.
Here is the code:
Server:
private TcpListener tcpListener;
private Thread listen;
private TcpListener tcpUser1, tcpUser2,tcpUser3;
NetworkStream User1Stream,User2Stream,User3Stream;
public event NetworkMessageStringDelegate MessageFromUser;
TcpClient client;
public void start(string ip){
IpHostEntry host = dns.GetHostEntry(dns.GetHostName());
IpAddress[] ip = host.AddressList;
serverStatus = "Server started with IP of: " + ip;
Thread.Sleep(1);
tcpUser1 = new TcpListener(IpAddress.Any, 4001);
listen = new Thread(new ThreadStart(() => ListenUser1(tcpUser1)));
listen.Start();
Thread.Sleep(1);
tcpUser2 = new TcpListener(IpAddress.Any, 4002);
listen = new Thread(new ThreadStart(() => ListenUser2(tcpUser2)));
listen.Start();
Thread.Sleep(1);
tcpUser3 = new TcpListener(IpAddress.Any, 4003);
listen = new Thread(new ThreadStart(() => ListenUser3(tcpUser3)));
listen.Start();
Thread.Sleep(1);
}
public void ListenUser3(TcpListener tmp){
tcpListener = (TcpListener)tmp;
Socket = "Listening for User3";
tcpUser3.Start();
Thread.Sleep(2);
while(true){
user3 = this.tcpListener.AcceptTcpClient();
Thread user3Thread = new Thread(new ParmeterizedThreadStart(receiveUser3Data));
user3Thread.Start(user3);
}
}
//Mostly from MS documenation
private void receiveUser3Data(object client){
client = (TcpClient)client;
User3Stream = client.getStream();
Socket = "Connected to User: " + client.Client.RemoteEndPoint.toString();
byte[] message = new byte[4096];
int total;
//This is the line it will display the socket message Twice. "Connected to User...."
while(true){
total = 0;
try{
do{
total = User3Stream.Read(message,0,4096);
}
while(user3.DataAvailable);
}
catch()
{
Socket = "Error State";
}
}
byte[] infoPacket = new byte[total];
Array.ConstrainedCopy(message,0,infoPacket,total);
if(MessageFromUser3 != null){
MessageFromUser?.Invoke(packet);
}
}
Client:
public void ConfigureUser3(){
try{
socket = new Network.TCPIPClient();
socket.ReceiveMessage() = new Newowrk.TCPIPClient.NetworkMessageStringDelgate(MessageFromserver);
socket.SendMessage() = new Newowrk.TCPIPClient.NetworkMessageStringDelgate(sendMessage);
userThread = new Thread(() => socket.Start("0.0.0.0),4054));
userThread.Start();
}
catch(Exception ex){
}
}
//This is where if I sleep it will send but it is still inconsident
private void SendMEssageToSever(object tmpVal){
object[] sendMessage = tmpVal as object[];
string tmpSendValue = tmpVal[0].ToString();
byte sendValue = Coonvert.ToByte(tmpVal[1]);
packetData[0] = 0;
packetData[1] = sendValue;
packetData[2] = Convert.ToByte(tmpSendValue);
socket.sendMessage = packetData;
}
private voide sendMessage(byte[] userMessage){
try{
if(socket){
outputMessage.Enqueue(userMessage);
while(outputMessage.Count > 0){
Byte[] sendMessage = outputMessage.Dequeue();
string message = ascII.GetString(sendMessage);
if(socket.Connected){
lock(socket){
socket.Send(sendMessage,sendMessage.length,0);
}
}
}
}
}
catch(Exception ex)
}
This code is essentially repeated for all users that are connected to the server.
The TcpListener has asynchronous methods like BeginAcceptTcpClient.
TcpClient.GetStream() (which is a NetworkStream) also has asynchronous methods like BeginRead.
I suggest you change your server code to use these and to store the user state in a class and pass this class to and fro between Begin* and End* methods.
You can support N number of users then, and don't have to repeat code for each user. You also don't have to have 3 different listeners for 3 connections. Have just one listener and accept clients over this one. The rest is two-way communication via TcpClient.GetStream()
Here is a minimal server example which listens on port 9988 (for only LoopBack, which means the local machine). You can of course change this.
There is no client example here. Only the server. Just copy/paste the code into your program.cs file in a console application.
I hope the comments are sufficient to explain the code.
I hope also, that this helps.
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
class Program
{
/// <summary>
/// Contains the state for a client connection
/// </summary>
public class ClientState
{
public const int ReceiveBufferSize = 8192;
// The buffer to receive in
internal byte[] receiveBuffer = new byte[ReceiveBufferSize];
// The TcpClient instance representing the remote end (connected client)
public TcpClient TcpClient { get; set; }
public byte[] GetReceiveBuffer()
{
return receiveBuffer;
}
}
// This method is invoked when data is received from a client
static void OnReceive(IAsyncResult asyncResult)
{
// The state parameter passed to the BeginRead method
// is provided here in the asyncResult.AsyncState property
ClientState clientState = asyncResult.AsyncState as ClientState;
int numberOfBytesReceived = clientState.TcpClient.GetStream().EndRead(asyncResult);
if (numberOfBytesReceived == 0)
{
// This means that the transmission is over
Console.WriteLine("Client disconnect: {0}", clientState.TcpClient.Client.RemoteEndPoint);
return;
}
// Now the receiveBuffer is filled with <numberOfBytesReceived> bytes received from the client.
// Do whatever is needed here.
Console.WriteLine("Received {0} bytes from {1}", numberOfBytesReceived, clientState.TcpClient.Client.RemoteEndPoint);
// We are also sending some information back:
StreamWriter streamWriter = new StreamWriter(clientState.TcpClient.GetStream());
streamWriter.WriteLine("The server has received {0} bytes from you! Keep up the good job!", numberOfBytesReceived);
streamWriter.Flush();
// Begin read again
clientState.TcpClient.GetStream().BeginRead(clientState.GetReceiveBuffer(), 0, ClientState.ReceiveBufferSize, OnReceive, clientState);
}
// This method is invoked when a new client connects
static void OnConnect(IAsyncResult asyncResult)
{
// The state parameter passed to the BeginAcceptTcpClient method
// is provided here in the asyncResult.AsyncState property
TcpListener tcpListener = asyncResult.AsyncState as TcpListener;
// Accept the TcpClient:
TcpClient newClient = tcpListener.EndAcceptTcpClient(asyncResult);
// Immediately begin accept a new tcp client.
// We do not want to cause any latency for new connection requests
tcpListener.BeginAcceptTcpClient(OnConnect, tcpListener);
// Create the client state to store information aboutn the client connection
ClientState clientState = new ClientState()
{
TcpClient = newClient
};
Console.WriteLine("A new client has connected. IP Address: {0}", newClient.Client.RemoteEndPoint);
// Start receiving data from the client
// Please note that we are passing the buffer (byte[]) of the client state
// We are also passing the clientState instance as the state parameter
// this state parameter is retrieved using asyncResult.AsyncState in the asynchronous callback (OnReceive)
newClient.GetStream().BeginRead(clientState.GetReceiveBuffer(), 0, ClientState.ReceiveBufferSize, OnReceive, clientState);
// Nothing else to do.
// The rest of the communication process will be handled by OnReceive()
}
static void Main()
{
// Start a tcp listener
TcpListener tcpListener = new TcpListener(IPAddress.Loopback, 9988);
tcpListener.Start();
// Begin accept a new tcp client, pass the listener as the state
// The state is retrieved using asyncResult.AsyncState in the asynchronous callback (OnConnect)
tcpListener.BeginAcceptTcpClient(OnConnect, tcpListener);
// That's it. We don't need anything else here, except wait and see.
Console.WriteLine("Server is listening on port 9988. Press enter to stop.");
Console.ReadLine();
}
}

C# How do I keep the socket or connection from closing?

I am using C# written application that connects to a few of our devices via IP. The application connects to the device just fine and we can send the required commands we need to configure it. The issue I run into is after about 40 seconds to a minute of not sending any commands the connection disconnects. I am wondering what I can do to keep the socket alive for at least a few minutes. Need some guidance on implementing a heartbeat, any assistance is appreciated.
Here is the code we are using.
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System.Globalization;
// State object for receiving data from remote device.
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousClient
{
// The port number for the remote device.
//private const int port = 1000;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone =
new ManualResetEvent(false);
private static ManualResetEvent sendDone =
new ManualResetEvent(false);
private static ManualResetEvent receiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
static Socket client;
internal static Boolean StartClient(string ip_address, int port)
{
// Connect to a remote device.
Boolean bRtnValue = false;
try
{
// Establish the remote endpoint for the socket.
// The name of the
// remote device is "host.contoso.com".
//IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
IPAddress ipAddress = IPAddress.Parse(ip_address);
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
client.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
// Send test data to the remote device.
string msg = ((char)2) + "S" + (char)13;
Send(client, msg);
//sendDone.WaitOne();
// Receive the response from the remote device.
Receive(client);
//receiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", response);
bRtnValue = true;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
//return value
return bRtnValue;
}
private static void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Receive(Socket client)
{
try
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Clear();
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
Console.WriteLine(DateTime.Now.ToLongTimeString() + ": " + state.sb.ToString());
//54A111503000000000017D8857E3
//IDSSSSSSSSSSSSSSSSTTTTCCCCKK 017D
if (state.sb.ToString(0, 2) == "54")
{
string hexString = state.sb.ToString(18, 4);
int num = Int32.Parse(hexString, NumberStyles.HexNumber);
double degreesF = ((double)num / 16.0) * 9.0 / 5.0 + 32.0;
string f = degreesF.ToString("#.#");
Console.WriteLine(" " + f);
}
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
else
{
Console.WriteLine("data");
// All the data has arrived; put it in response.
if (state.sb.Length > 1)
{
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
internal void StartListening(string ip_address, int port)
{
StartClient(ip_address, port);
}
You need to implement a timer that simulates a heart beat when the is no data to be sent, I.e. after the last packet you send you start a timer for a couple of seconds. If you have data to send before that you cancel it, send your data and restart the timer again. If the timer times out you send a dummy data packet and restart the timer to do it again after the same time out period.
The reason for this is what's known as timeout, an important feature of (to my knowledge) every network protocol in place to handle when the other end of the connection aborts, disconnects, or otherwise stops transmitting data altogether. There is, after all, no way to transmit a packet saying "my cat just knocked my router down, abort connection."
What you need to do is, as mentioned in comments, send periodic no-op (ie do-nothing) commands through the network, just to keep it from timing out. Such packets are called "heartbeat" or "keepalive" signals, and should be sent far more often than the actual timeout, in case any get lost or arrive late. This is best done by starting up a seondary thread which does nothing but send heartbeats while the connection is marked as open.

Java TCP Client and C# Server; Client Only Receives Message After Server Closes

I have written an asynchronous server in C# and a TCP client in Java for and an Android app. The client can send messages fine to the server and they are received when they are sent. However when I send a message from the server to the client, the client only displays the message after the server is shutdown (i.e. when the socket closes).
The strange thing is that I have written a client in C# as well and that receives messages as soon as they are sent.
The C# server and client both use the asynchronous begin* and end* methods and the Java client uses a stream reader/writer.
Can anyone please suggest why the Java client is behaving in this way and how to remedy this?
Thanks.
Client Code:
public void run() {
mRun = true;
try {
//here you must put your computer's IP address.
InetAddress serverAddr = InetAddress.getByName(SERVERIP);
Log.e("TCP Client", "C: Connecting...");
//create a socket to make the connection with the server
socket = new Socket(serverAddr, SERVERPORT);
try {
if (out != null)
{
//send the message to the server
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
Log.e("TCP Client", "C: Sent.");
Log.e("TCP Client", "C: Done.");
}
//receive the message which the server sends back
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//in this while the client listens for the messages sent by the server
while (mRun) {
serverMessage = in.readLine();
if (serverMessage != null && mMessageListener != null) {
//call the method messageReceived from MyActivity class
mMessageListener.messageReceived(serverMessage);
serverMessage = null;
}
}
Log.e("RESPONSE FROM SERVER", "S: Received Message: '" + serverMessage + "'");
} catch (Exception e) {
Log.e("TCP", "S: Error", e);
} finally {
//the socket must be closed. It is not possible to reconnect to this socket
// after it is closed, which means a new socket instance has to be created.
socket.close();
}
} catch (Exception e) {
Log.e("TCP", "C: Error", e);
}
}
Server Code:
public void Send(String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
socket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), socket);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
//Retrieve the socket from the state object.
Socket clientSocket = (Socket)ar.AsyncState;
//send the data
int bytesSent = clientSocket.EndSend(ar);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
My guess is that the data you send from the server doesn't end with an EOL sequence (\n or \r\n). So the readLine() method at client-side never returns, since it can only return when it's sure the line is terminated (i.e. when an EOL sequence is received, or the connection is closed).

HTTP GET (with Header 'Connection: keep alive') over TCP

I want to do the following with a raw C# socket. I understand that usually the most appropriate way is via HTTP, with a HTTP client. The browser understands that this connection must be kept open in some way.
http://server.domain.com/protocol/do_something.txt
I am trying the following in C#, but have had no luck. What am I doing wrong? Is there a header missing? Should I be encoding what I'm sending to the server in some way? For the ReceiverSocket client, I'm using the following code, but it's just a very standard asynchronous socket client: https://stackoverflow.com/a/10390066/971580
ReceiverSocket socket = new ReceiverSocket("server.domain.com", 80);
socket.Connect();
System.Threading.Thread.Sleep(1000);
String message = "GET /protocol/do_something.txt HTTP/1.1";
message += "\r\n";
message += "\r\n";
socket.Send(message);
The socket can connect successfully, but I don't get any response when I send anything to the server. This is how I am connecting, sending and receiving.t (Apologies: I tried to do this in snippets, rather than including all the methods, but it looked horrid. . .)
public ReceiverSocket(String address, int port) : base(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
{
messageQueue = new Queue<MessageBase>();
IPHostEntry ipHostInfo = Dns.GetHostEntry(address);
IPAddress ipAddress = ipHostInfo.AddressList[0];
remoteEP = new IPEndPoint(ipAddress, port);
}
public void Connect()
{
this.BeginConnect(remoteEP, ConnectCallback, this);
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;
if (client.Connected)
{
client.EndConnect(ar);
Console.WriteLine("Connect Callback - Connected");
StateObject state = new StateObject();
state.workSocket = client;
state.BufferSize = 8192;
if (SocketConnected != null)
SocketConnected(client);
client.BeginReceive(state.Buffer, state.readOffset, state.BufferSize - state.readOffset, 0, ReceiveCallback, state);
}
else
{
Thread.Sleep(5000);
Connect();
}
}
catch (Exception ex)
{
Reconnect();
}
}
private void ReceiveCallback(IAsyncResult ar)
{
Console.WriteLine("Never gets here. . . ");
try
{
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
if (client.Connected)
{
int bytesRead = client.EndReceive(ar);
foreach (MessageBase msg in MessageBase.Receive(client, bytesRead, state))
{
// Add objects to the message queue
lock (this.messageQueue)
this.messageQueue.Enqueue(msg);
}
if (DataRecieved != null)
DataRecieved(client, null);
client.BeginReceive(state.Buffer, state.readOffset, state.BufferSize - state.readOffset, 0, ReceiveCallback, state);
}
else
{
Reconnect();
}
}
catch (SocketException)
{
Reconnect();
}
}
public void Send(String msg)
{
try
{
byte[] bytes = GetBytes(msg);
if (this.Connected)
{
Console.WriteLine("Sending: " + msg);
this.BeginSend(bytes, 0, bytes.Length, 0, SendCallback, this);
}
else
{
Reconnect();
}
}
catch (SocketException sox)
{
Reconnect();
}
catch (Exception ex)
{
int i = 0;
}
}
static byte[] GetBytes(string str)
{
byte[] bytes = new byte[str.Length * sizeof(char)];
System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
}
public class StateObject
{
public Socket workSocket = null;
public int readOffset = 0;
public StringBuilder sb = new StringBuilder();
private int bufferSize = 0;
public int BufferSize
{
set
{
this.bufferSize = value;
buffer = new byte[this.bufferSize];
}
get { return this.bufferSize; }
}
private byte[] buffer = null;
public byte[] Buffer
{
get { return this.buffer; }
}
}
Shouldn't the fact that I haven't included the message += "Connection: close" header mean that the socket should just start sending whatever data it has asynchronously? Just to note also: I can connect successfuly using Telnet and send the data, just not with a socket yet!
Any pointers at all would be appreciated.
Thanks.
Do NOT call Send() until ConnectCallback() is called first, otherwise you risk sending your data prematurely. Using Sleep() to wait for the connection is wrong.
Do NOT call BeginReceive() until after Send() has finished sending the data.
Because you are using HTTP 1.1, then yes, the connection is kept alive by default if you are connecting to an HTTP 1.1 server. The server's Connection response header will indicate whether the server is actually keeping the connection open or not.
Also, as stated by someone else, HTTP 1.1 requests MUST have a Host header or else the request is malformed and can be rejected/ignored by the server. HTTP 1.1 has a notion of virtual hosts running on the same IP, so the Host header tells the server which host the client wants to talk to.

Categories

Resources