I'm writing a websockets-client. I have two problems :
When I close a window of my application a server goes down
Server did not receiving messages but Client always receives a greeting message from server.
System.Exception : You must send data by websocket after websocket is
opened
Client on C# (Websocket4Net lib)
private static void _clientSocket_Closed(object sender, EventArgs e)
{
if (_clientSocket.State == WebSocket4Net.WebSocketState.Open)
{
_clientSocket.Close("Closed by user");
}
}
public static void WebRequest(string url, dutyObject objToSend)
{
_clientSocket = new WebSocket(url);
_clientSocket.MessageReceived += _clientSocket_MessageReceived;
_clientSocket.DataReceived += _clientSocket_DataReceived;
_clientSocket.Closed += _clientSocket_Closed;
_clientSocket.Error += new EventHandler<SuperSocket.ClientEngine.ErrorEventArgs>(_clientSocket_Error);
_clientSocket.Open();
var jsonMessage = JsonSerializeHelper.Serialize(objToSend);
_clientSocket.Send(jsonMessage);
}
Server on php
class Server extends WebSocketServer
{
protected function serverCreated()
{
}
/**
* This is run when server is receiving data.
*/
protected function process($connected_user, $message)
{
$this->send($connected_user,"[+]".$message); //just echo reply
}
/**
* This is run when socket connection is established. Send a greeting message
*/
protected function connected($connected_user)
{
$welcome_message = 'Welcome to Service. Service works with JSON. Be careful!';
$this->send($connected_user, $welcome_message);
}
protected function closed($connected_user)
{
$this->stdout("User closed connection \n");
}
}
UPDATE on client.
while (_clientSocket.State != WebSocketState.Open)
{
if (_clientSocket.State == WebSocket4Net.WebSocketState.Open)
{
Console.WriteLine(_clientSocket.State);
_clientSocket.Send(ecn.GetBytes(jsonMessage), 0, ecn.GetBytes(jsonMessage).Length);
}
else
{
Console.WriteLine("E: " + _clientSocket.State);
//_clientSocket.Close();
}
}
And it permanent says "Connecting".
I suspect this is probably with an error with handshake - when looking at the code I saw that if no handshake was made this error is thrown
private bool EnsureWebSocketOpen()
{
if (!Handshaked)
{
OnError(new Exception(m_NotOpenSendingMessage));
return false;
}
return true;
}
Related
I am making a client/server chat application using TcpClient and TcpListener classes. It should be able to transfer text messages and files between client and server. I am able to handle text messages by making a thread for each separate client and then making a secondary thread for receiving incoming message making primary thread reserved for sending messages. Now if I would be able to identify that incoming message is a file and not a text message then I know how to handle it using NetworkStream and FileStream. But I am unable to do so. Code to handle incoming file is here. Also please tell me if there are any limitations using NetworkStream for FTP.
Answer: Build your own protocol.
By building your own good communication protocol you can control all data/message flow.
For example;
1-User wants to send a file to server
2-Client sends a command to inform the server that it will send a file.Like ;
#File#filename;filesize;
3-Server sends a ready message back to client #FileAccepted#
4-Server begins to listen buffer packages and when it receives writes them to an stream.
5-When client receives {#FileAccepted#} command begins to send packages to server. Be sure their buffer sizes are same.
6-When all bytes complete client sends #FileEnd# in diffrent buffer (i use 256 for commands and 1024 for file transfer)
7-When server receives 256 byte command looks if its the #FileEnd# command and is true flushes file stream and closes connection.
I recomment you use Async
Listen for connections on server like this
server.BeginAcceptTcpClient(ServerAcceptEnd,server);
And when a connection is present
public void ServerAcceptEnd(IAsyncResult ar)
{
if(!ar.IsCompleted)
{
//Something went wrong
AcceptServer();
return;
}
try
{
var cli = servertc.EndAcceptTcpClient(ar);
if(cli.Connected)
{
//Get the first Command
cli.GetStream().BeginRead(serverredbuffer,0,serverredbuffer.Length,ServerFirstReadEnd,cli);
}
else
{
//Connection was not successfull log and wait
AcceptServer();
}
}
catch(Exceiption ex)
{
//An error occur log and wait for new connections
AcceptServer();
}
}
When first command received ;
public void ServerFirstReadEnd(IAsyncResult ar)
{
if(!ar.IsCompleted)
{
//Something went wrong
AcceptServer();
return;
}
try
{
TcpClient cli = (TcpClient)ar.AsyncState;
int read = cli.GetStream().EndRead(ar);
string req = toString(serverredbuffer);
if(req.StartsWith("#File#"))
{
//File Received
string filename = req.Replace("#File#","");
string[] spp = filename.Split(';');
filename = spp[0];
serverreceivetotalbytes = Convert.ToInt64(spp[1]);
cli.GetStream().Write(toByte("#FileAccepted#",256),0,256);
cli.GetStream().BeginRead(serverreceivebuffer,0,1024,ServerReadFileCyle,cli)
}
else
{
//Message Received
}
}
catch(Exception ex)
{
//An error occur log and wait for new connections
AcceptServer();
}
}
File receive method ;
public void ServerReadFileCyle(IAsyncResult ar)
{
TcpClient cli = (TcpClient)ar.AsyncState;
if(ar.IsCompleted)
{
int read = cli.GetStream().EndRead(ar);
if(read == 256)
{
try
{
string res = toString(serverreceivebuffer);
if(res == "#FileEnd#")
read = 0;
}
catch
{
}
}
if(read > 0)
{
serverfile.Write(serverreceivebuffer,0,read);
cli.GetStream().BeginRead(serverreceivebuffer,0,1024,ServerReadFileCyle,cli);
}
else
{
serverfile.Flush();
serverfile.Dispose();
AcceptServer();
}
}
}
This part was server side.And for client side;
When sending a file first send a information to server for file and then wait for a response from server.
try
{
System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
ofd.Multiselect = false;
ofd.FileName="";
if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
filesendpath = ofd.FileName;
senderfilestream = System.IO.File.Open(filesendpath,System.IO.FileMode.Open);
sendertotalbytes = senderfilestream.Length;
filesendcommand = "#File#" + System.IO.Path.GetFileName(filesendpath) + ";" + senderfilestream.Length;
senderfilestream.Position = 0;
sendertc.BeginConnect(ip.Address,55502,FileConnect,null);
}
else
{
//No file selected
}
}
catch(Exception ex)
{
//Error connecting log the error
}
If connection is successfull , then send the file command and wait for response ;
public void FileConnect(IAsyncResult ar)
{
if(ar.IsCompleted)
{
sender.EndConnect(ar);
if(sender.Connected)
{
sender.GetStream().Write(toByte(filesendcommand,256),0,256);
sender.GetStream().BeginRead(ComputerNameBuffer,0,256,FileSendCyleStarter,null);
}
}
}
When response received look if it is successfull an accepted;
public void FileSendCyleStarter(IAsyncResult ar)
{
if(ar.IsCompleted)
{
if(sender.Connected)
{
string kabul = toString(ComputerNameBuffer);
if(kabul == "#FileAccepted#")
{
senderfilestream.BeginRead(filesendbuffer,0,1024,FileSendCyle,null);
}
}
}
}
Sending a file has three steps;
1-Read a chunk for a start
2-Then send the chunk to server.if its completed send #FileEnd# command and skip step 3
3-Read next chunk of file
4-Return step 2 if file isnt completed
Step 1 :
senderfilestream.BeginRead(filesendbuffer,0,1024,FileSendCyle,null);
Step 2-4 :
public void FileSendCyle(IAsyncResult ar)
{
if(ar.IsCompleted)
{
if(sendertc.Connected)
{
int read = senderfilestream.EndRead(ar);
if(read > 0)
{
sendertc.GetStream().BeginWrite(filesendbuffer,0,read,FileSendCyle2,null);
}
else
{
sendertc.GetStream().Write(toByte("#FileEnd#",256),0,256);
}
}
}
}
Step 3 :
public void FileSendCyle2(IAsyncResult ar)
{
if(ar.IsCompleted)
{
if(sendertc.Connected)
{
sendertc.GetStream().EndWrite(ar);
senderfilestream.BeginRead(filesendbuffer,0,1024,FileSendCyle,null);
}
}
}
In abowe example there are two methods called toString() and toByte().I used them for converting strings to bytes and bytes to strings.Here are them ;
public string toString(byte[] buffer)
{
return Encoding.UTF8.GetString(buffer).Replace("\0","");
}
public byte[] toByte(string str,int bufferlenght)
{
byte[] buffer = new byte[256];
Encoding.UTF8.GetBytes(str,0,str.Length,buffer,0);
return buffer;
}
The code abowe example isn't perfect and need lots of error handling and flow controls.I write theese to give you an idea and a jump start.
Hope any part of it helps anybody ^_^
I am building an application , in which I need to have a c# server and nodejs client. I have built the components , but I am always getting ECONNREFUSED error. any leads when I can except this error?
FYI,
I am able to connect to c# server from c# client. similarly. I am able to connect to nodejs tcp server from nodejs tcp client. however the I am facing error with this mixture.
hey sorry for not adding code earlier.
the following is the c# server code:
using System;
using System.Text;
using AsyncClientServerLib.Server;
using System.Net;
using AsyncClientServerLib.Message;
using SocketServerLib.SocketHandler;
using SocketServerLib.Server;
namespace TestApp
{
delegate void SetTextCallback(string text);
public partial class FormServer : Form
{
private BasicSocketServer server = null;
private Guid serverGuid = Guid.Empty;
public FormServer()
{
InitializeComponent();
}
protected override void OnClosed(EventArgs e)
{
if (this.server != null)
{
this.server.Dispose();
}
base.OnClosed(e);
}
private void buttonStart_Click(object sender, EventArgs e)
{
this.serverGuid = Guid.NewGuid();
this.server = new BasicSocketServer();
this.server.ReceiveMessageEvent += new SocketServerLib.SocketHandler.ReceiveMessageDelegate(server_ReceiveMessageEvent);
this.server.ConnectionEvent += new SocketConnectionDelegate(server_ConnectionEvent);
this.server.CloseConnectionEvent += new SocketConnectionDelegate(server_CloseConnectionEvent);
this.server.Init(new IPEndPoint(IPAddress.Loopback, 2147));
this.server.StartUp();
this.buttonStart.Enabled = false;
this.buttonStop.Enabled = true;
this.buttonSend.Enabled = true;
MessageBox.Show("Server Started");
}
void server_CloseConnectionEvent(AbstractTcpSocketClientHandler handler)
{
MessageBox.Show(string.Format("A client is disconnected from the server"), "Socket Server", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
void server_ConnectionEvent(AbstractTcpSocketClientHandler handler)
{
MessageBox.Show(string.Format("A client is connected to the server"), "Socket Server", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
void server_ReceiveMessageEvent(SocketServerLib.SocketHandler.AbstractTcpSocketClientHandler handler, SocketServerLib.Message.AbstractMessage message)
{
BasicMessage receivedMessage = (BasicMessage)message;
byte[] buffer = receivedMessage.GetBuffer();
if (buffer.Length > 1000)
{
MessageBox.Show(string.Format("Received a long message of {0} bytes", receivedMessage.MessageLength), "Socket Server",
MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
string s = System.Text.ASCIIEncoding.Unicode.GetString(buffer);
this.SetReceivedText(s);
}
private void buttonStop_Click(object sender, EventArgs e)
{
this.server.Shutdown();
this.server.Dispose();
this.server = null;
this.buttonStart.Enabled = true;
this.buttonStop.Enabled = false;
this.buttonStop.Enabled = false;
MessageBox.Show("Server Stopped");
}
private void SetReceivedText(string text)
{
if (this.textBoxReceived.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetReceivedText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBoxReceived.Text = text;
}
}
private void buttonSend_Click(object sender, EventArgs e)
{
ClientInfo[] clientList = this.server.GetClientList();
if (clientList.Length == 0)
{
MessageBox.Show("The client is not connected", "Socket Server", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
AbstractTcpSocketClientHandler clientHandler = clientList[0].TcpSocketClientHandler;
string s = this.textBoxSend.Text;
byte[] buffer = System.Text.ASCIIEncoding.Unicode.GetBytes(s);
BasicMessage message = new BasicMessage(this.serverGuid, buffer);
clientHandler.SendAsync(message);
}
}
}
The following is the nodejs client code.
var sys = require("sys"),
net = require("net");
var client = net.createConnection(2147);
client.setEncoding("UTF8");
client.addListener("connect", function() {
sys.puts("Client connected.");
// close connection after 2sec
setTimeout(function() {
sys.puts("Sent to server: close");
client.write("close", "UTF8");
}, 2000);
});
client.addListener("data", function(data) {
sys.puts("Response from server: " + data);
if (data == "close") client.end();
});
client.addListener("close", function(data) {
sys.puts("Disconnected from server");
});
I am able to solve the issue. This is just a overlook issue. In server code , I am using lan address assigned to my machine , but in client side , I am using 127.0.0.1 . when I changed the both to same value , I amnot getting econnrefused error.
Now I am able to send and receive data. However I am getting ECONNRESET error very frequently. any leads?
I have a problem with my TCPIP connection Form programm.
I have a code, where I'm trying to send and receive some data from server.
The main problem of my app is how to reconcile some threads:
myListenThread - to listening data from server
myReadStreamThread - to read data from server
System.Threading.Thread - main thread eg. to write data to server
captureThread - to do another things like capturing images from camera
Part of my code:
private void buttonConnect_Click(object sender, EventArgs e)
{
try
{
Connect();
Connected = true;
this.myListenThread = new Thread(new ThreadStart(Listen));
this.myListenThread.Start();
}
catch
{
MessageBox.Show("Invalid host! Try again.");
}
}
private void Listen()
{
this.myReadStreamThread = new Thread(new ThreadStart(ReadStream));
this.myReadStreamThread.Start();
while (Connected)
{
if (!myReadClient.Connected)
{
Connect();
}
}
}
private void Connect()
{
IPAddress IP = IPAddress.Parse(textboxIP.Text);
int PORT = Convert.ToInt32(textboxPORT.Text);
this.myReadClient = new TcpClient();
this.myReadClient.Connect(IP, PORT);//SOMETIMES HERE'S AN ERROR
this.myStream = this.myReadClient.GetStream();
Properties.Settings.Default.IP = Convert.ToString(IP);
Properties.Settings.Default.PORT = Convert.ToString(PORT);
Properties.Settings.Default.Save();
}
private void ReadStream()
{
while (true)
{
try
{
this.myReadBuffer = new byte[this.myReadClient.ReceiveBufferSize];
this.myBufferSize = myStream.Read(myReadBuffer, 0, this.myReadClient.ReceiveBufferSize);
if (myBufferSize != 0)
{
this.myString = Encoding.ASCII.GetString(myReadBuffer);
//myDelegate myDel;
//myDel = new myDelegate(Print);
//richtextboxRead.Invoke(myDel);
}
}
catch
{
break;
}
}
}
All is working correct when I'm connecting to server, but when I want to send some string the problem appears because of threads.
I decided to send string, by clicking Button3 and waiting until I receive string "1" from server using while loop:
private void button3_Click(object sender, EventArgs e)
{
this.captureThread = new Thread(new ThreadStart(() => this.newGame()));
this.captureThread.Start();
}
private bool newGame()
{
string command = "12345abc";
if (Connected)
{
WriteStream(command);
}
while (myBufferSize == 0 && myString !="1") { }
Thread.Sleep(2000);
...//doing other things
}
private void WriteStream(string command)
{
Connect();
this.myWriteBuffer = Encoding.ASCII.GetBytes(command);
this.myStream.Write(this.myWriteBuffer, 0, command.Length);
}
And the problem with connection and data send/receive problem appears, when it should write my string "command" - it doesn't react. MyBufferSize is always 0 and myString is always null. Sometimes an Error about connection appears when I click Button3 (assigned in code). I think it is because in captureThread I can't see any data from another threads. How to solve it?
I use SSH.NET to create SSH Tunnels in .Net-Applications. In a ConsoleApplication / Windows-Service of mine this library works just as expected.
Now I coded a WPF Application that creates an SSH-Tunnel to remotly access a MySQL Database.
I can access the database and execute my SQL Statements just fine.
But if I try to close the tunnel after disconnecting from the database i first get an SocketException: 10004 A blocking operation was interrupted by a call to WSACancelBlockingCall
an then several more Exceptions:
SocketException: 10053 An established connection was stopped by the software in your host computer, possibly because of a data transmission time-out or protocol error.
Renci.SshNet.Common.SshConnectionException: Bad packet length XXX (XXX is an random integer)
Renci.SshNet.Common.SshConnectionException: Client not connected.
I use following Code for opening / closing the tunnel:
public class SSHTunnelBuilder
{
private SshClient client;
private ForwardedPort port;
public SSHTunnelBuilder()
{
}
public void CloseTunnel()
{
if (this.port != null && this.port.IsStarted)
{
this.port.Stop();
}
if (this.client != null && this.client.ForwardedPorts.Contains(this.port))
{
this.client.RemoveForwardedPort(this.port);
}
this.port = null;
if (this.client != null)
{
if (this.client.IsConnected)
{
this.client.Disconnect();
}
this.client.Dispose();
this.client = null;
}
}
public void OpenTunnel()
{
if (this.client == null)
{
this.client = new SshClient("host", "usr", "pwd");
}
if (!this.client.IsConnected)
{
this.client.Connect();
}
if (this.port == null)
{
this.port = new ForwardedPortLocal("XXX.XXX.XXX.XXX", 10000, "YYY.YYY.YYY.YYY", 3306);
}
if (!this.client.ForwardedPorts.Contains(this.port))
{
this.client.AddForwardedPort(this.port);
}
if (!this.port.IsStarted)
{
this.port.Start();
}
}
}
And the SSHTunnelBuilder is used inside a TASK like this:
private void SomeMethod()
{
Task.Factory.StartNew(
new Action(() =>
{
SSHTunnelBuilder ssh = new SSHTunnelBuilder();
try
{
ssh.OpenTunnel();
// Do something
ssh.CloseTunnel();
ssh.Dispose();
ssh = null;
}
catch (Exception e)
{
ssh.CloseTunnel();
ssh.Dispose();
ssh = null;
}
}));
}
Can Somebody explain to me how to get rid of those Exceptions?
UPDATE:
Due to problems with the admins here on Stackoverflow, I have posted a very trimmed-down version of the same problem on MSDN forum. This text below used MyNetworking.dll, but that is not the problem. Here is a very slimmed Client-Server thing and the problem is exactly the same. Feel free to try it out =)
http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/d3d33eb9-7dce-4313-929e-a8a63d0f1e03
/UPDATE
So, I have a strange error.
Normally, we have a DLL that handles our networking. Lets call that MyNetworking.dll. We use it everywhere in our servers and clients and have done so for 5 years. I haven't had a problem with it, until now.
I have an "XMLPoller", that reads XML from a MySQL database, serializes that into a byte[] array and sends it over the network. These particular XML messages is 627 bytes in serialized form.
The XMLPoller connects to a port on a "remote server" (that happens to be localhost) and sends the packets, one at a time. At exactly packet nbr 105 the connection is closed. 104 packets are sent from XMLPoller and received by the Server. 104 x 627 = 65208 bytes. But packet 105, when the total number of bytes sent would be 65835 the connection is closed with this error:
System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult)
at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)
This is the error on the server. However, I have stepped through the XMLPoller (client), and I see when the last 627 bytes are sent (thus sending up til 65835 bytes) and I see no errors on the client, it passes sending without problems.
UPDATE 20:15 SWEDISH TIME
I also get this error in the Client when I debug a little more:
Unable to read data from the transport connection: An established connection was aborted by the software in your host machine.
I think I have confirmed that it is in the Client the error exists. I am stepping through the code and before any Exceptions are caught on the server, I get an exception on the Client as stated above.
/ENDUPDATE
It seems to me that the Server never receives it, getting the error above. The server gets the connection closed because of something happening on the Client. However, the error on the client is in TCPInput; the stream reading data is dead for some reason?
I am not buffering anything in MyNetworking.dll.
When I get a new connection on a Socket (on the Server), I do this code:
public void setConnected(Socket thisClient)
{
NetworkStream stream = new NetworkStream(thisClient);
socket = thisClient;
output = new TCPOutput(stream, outputHandler,this);
remoteIP = this.socket.RemoteEndPoint.ToString();
changeState(State.Connected);
try
{
stream.BeginRead(inputBuffer, 0, 5000, new AsyncCallback(OnDataReceived), null);
}
catch (Exception e)
{
this.disconnect();
}
}
and then, the OnDataReceived method (where the data is actually received):
public void OnDataReceived(IAsyncResult asyn)
{
int nbrRead = 0;
byte[] tmp = null;
try
{
nbrRead = stream.EndRead(asyn);
tmp = new byte[nbrRead];
}
catch (Exception e)
{
// *** HERE IS WHERE THE EXCEPTION IS CAUGHT ***
System.Diagnostics.Debugger.Log(0, "Bla1", e.ToString());
this.disconnect();
}
if (nbrRead > 0)
{
try
{
Array.Copy(inputBuffer, 0, tmp, 0, nbrRead);
}
catch(Exception e)
{
System.Diagnostics.Debugger.Log(0, "Bla2", e.ToString());
this.disconnect();
}
preProcessMessage(tmp);
try
{
stream.BeginRead(inputBuffer, 0, 5000, new AsyncCallback(OnDataReceived), new object());
}
catch(Exception e)
{
System.Diagnostics.Debugger.Log(0, "Bla3", e.ToString());
this.disconnect();
}
}
else
this.disconnect();
}
Right now Im sort of clueless as to what is going on... Any ideas?
UPDATE 1:
Client code for sending data:
public bool sendData(byte[] data)
{
if(this.state == State.Connected)
{
if (data != null && data.Length > 0)
{
try
{
//data = Crypto.Encrypt("a1s2d3", data);
outputStream.Write(data, 0, data.Length);
}
catch(Exception e)
{
System.Diagnostics.Debug.WriteLine("ClientHandler.sendData> " + e.ToString());
}
//parent.outDataLog(data.Length);
}
}
return true;
}
Update 2
I tried to do a Flush on the outgoing stream from the client - no effect:
public bool sendData(byte[] data)
{
if(this.state == State.Connected)
{
if (data != null && data.Length > 0)
{
try
{
//data = Crypto.Encrypt("a1s2d3", data);
outputStream.Write(data, 0, data.Length);
outputStream.Flush();
}
catch(Exception e)
{
System.Diagnostics.Debug.WriteLine("ClientHandler.sendData> " + e.ToString());
}
//parent.outDataLog(data.Length);
}
}
return true;
}
UPDATE 3: Posting more code as per request
This code is old and not the pretties in the world, I know. But it has been working very well for 5 years so =)
ClientHandler.cs (what the actual Client is using for sending etc)
using System;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace tWorks.tNetworking.tNetworkingCF
{
/// <summary>
/// Summary description for connectionHandler.
/// </summary>
public class ClientHandler
{
#region Fields (17)
string address;
Connector connector;
DataHandler dataHandler;
int id;
TCPInput input;
int interval;
string localAddress;
IPEndPoint localPoint;
int localPort;
NetworkStream outputStream;
public TTCPClientInterface parent;
int port;
tWorks.tNetworking.Protocol.Protocol protocol;
bool reconnect;
string remoteIP;
Socket socket;
public State state;
#endregion Fields
#region Enums (1)
public enum State {Disconnected,Connecting,Connected}
#endregion Enums
#region Constructors (4)
public ClientHandler(int id, TTCPClientInterface parent, Socket socket, tWorks.tNetworking.Protocol.Protocol protocol)
{
this.id=id;
this.parent = parent;
this.protocol = protocol;
dataHandler = new DataHandler(protocol, this);
setConnected(socket);
}
public ClientHandler(int id, TTCPClientInterface parent, Protocol.Protocol protocol)
{
this.id=id;
this.parent = parent;
this.protocol = protocol;
dataHandler = new DataHandler(protocol, this);
state = State.Disconnected;
}
public ClientHandler(int id, TTCPClientInterface parent, Socket socket)
{
this.id=id;
this.parent = parent;
setConnected(socket);
}
public ClientHandler(int id, TTCPClientInterface parent)
{
this.id=id;
this.parent = parent;
this.protocol = null;
changeState(State.Disconnected);
}
#endregion Constructors
#region Delegates and Events (4)
// Delegates (2)
public delegate void ConnectionLostDelegate(string message);
public delegate void exceptionDelegate(Exception ex);
// Events (2)
public event exceptionDelegate ConnectionFailed;
public event ConnectionLostDelegate ConnectionLostEvent;
#endregion Delegates and Events
#region Methods (17)
// Public Methods (16)
public void connect(string address, int port, int retryInterval, bool reestablish)
{
System.Random rand = new Random();
localPort = rand.Next(40000, 60000);
IPAddress localIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; // new IPAddress(Dns.GetHostByName(Dns.GetHostName()).AddressList[0].Address);
connect(address, port, retryInterval, reestablish, localIP.ToString(), localPort);
}
/// <summary>
/// Will connect to the address and port specified. If connection failed a new attempt will be made according to the Interval parameter.
/// If connection is lost attempts to reastablish it will be made if Reestablish is set to true.
/// </summary>
/// <param name="address"></param>
/// <param name="port"></param>
/// <param name="retryInterval"></param>
/// <param name="reestablish"></param>
public void connect(string address, int port, int retryInterval, bool reestablish, string localAddress, int localPort)
{
this.reconnect = reestablish;
this.address = address;
this.port = port;
this.interval = retryInterval;
this.localAddress = localAddress;
this.localPort = localPort;
changeState(State.Connecting);
connector = new Connector(address, port, this, interval, localPoint, reestablish);
connector.Connect();
}
public void disconnect()
{
reconnect = false;
if (connector != null)
{
connector.stopConnecting();
}
setDisconnected();
}
public void dispose()
{
}
public void failedConnect(Exception e)
{
if (ConnectionFailed != null)
ConnectionFailed(e);
}
public int getID()
{
return this.id;
}
public string getIP()
{
return remoteIP;
}
public bool isConnected()
{
return this.state == State.Connected;
}
public void outDataLog(int nbrBytes)
{
parent.outDataLog(nbrBytes, id);
}
public void preProcessMessage(byte[] data)
{
//data = Crypto.Decrypt("a1s2d3", data);
if(protocol != null)
dataHandler.addData(data);
else
processMessage(data);
}
public void processMessage(byte[] data)
{
parent.processMessage(data,this);
}
public bool sendData(byte[] data)
{
if(this.state == State.Connected)
{
if (data != null && data.Length > 0)
{
try
{
//data = Crypto.Encrypt("a1s2d3", data);
outputStream.Write(data, 0, data.Length);
outputStream.Flush();
}
catch(Exception e)
{
System.Diagnostics.Debug.WriteLine("ClientHandler.sendData> " + e.ToString());
}
//parent.outDataLog(data.Length);
}
}
return true;
}
public void setConnected(Socket thisClient)
{
socket = thisClient;
outputStream = new NetworkStream(thisClient);
input = new TCPInput(outputStream, this);
remoteIP = this.socket.RemoteEndPoint.ToString();
changeState(State.Connected);
}
public void setDisconnected()
{
try
{
if (this.state == State.Connected)
{
changeState(State.Disconnected);
//socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
}
catch { }
if (reconnect)
this.connect(address, port, interval, true, localAddress, localPort);
}
public void stopConnect()
{
connector.stopConnecting();
changeState(State.Disconnected);
}
public override string ToString()
{
string returnString = "(D)";
if(this.state == State.Connected)
returnString = this.getIP();
return returnString;
}
// Private Methods (1)
private void changeState(State state)
{
if (this.state == State.Connected && state == State.Disconnected)
{
if (ConnectionLostEvent != null)
ConnectionLostEvent("Uppkoppling bröts.");
}
this.state = state;
parent.connStateChange(this);
}
#endregion Methods
}
}
This is TCPInput.cs that is listening on incoming data and forwarding that to the ClientHandler (seen above):
using System;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace tWorks.tNetworking.tNetworkingCF
{
public class TCPInput
{
NetworkStream stream;
ClientHandler client;
public TCPInput(NetworkStream nS, ClientHandler client)
{
stream = nS;
this.client = client;
Thread t = new Thread(new ThreadStart(run));
t.IsBackground = true;
t.Name = "TCPInput";
t.Start();
}
public void run()
{
bool continueRead = true;
byte[] readBuffer = new byte[32768];
byte[] receivedBuffer = null;
int nbrBytesRead = 0;
int receivedBufferPos = 0;
while(continueRead)
{
try
{
nbrBytesRead = 0;
nbrBytesRead = stream.Read(readBuffer, 0, 10000);
receivedBuffer = new byte[nbrBytesRead];
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("TCPInput> Exception when stream.Read: " + e.ToString());
continueRead = false;
}
if(nbrBytesRead > 0)
{
try
{
Array.Copy(readBuffer, 0, receivedBuffer, receivedBufferPos, nbrBytesRead);
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("TCPInput> Exception when Array.Copy: " + e.ToString());
continueRead = false;
}
client.preProcessMessage(receivedBuffer);
}
else
{
// *** I can break here, the nbrOfBytes read is 0 when this whole thing explodes =)
System.Diagnostics.Debug.WriteLine("TCPInput> Number of bytes read == 0! Setting continueRead = false");
continueRead = false;
}
}
client.setDisconnected();
}
}
}
The problem is in your other code, the 'client'. It closes the connection after sending all the 'packets'. You must wait until the server has received all of them. A simple approach, beyond negotiating it explicitly, is to wait for the server to close the connection.
That number ("thus sending up til 65835 bytes") is magically close to 2^16-1 (65535) -- looks like just one packet over!
(I'm assuming it's just the larger size that made things go kaboom! -- this can be tested reliably.)
I suspect there is an unsigned 16-bit variable used (in the library) where you need something with more range. Perhaps you can "empty" the internals of the library periodically or perform the operation in multiple connection? (Okay, just trying to throw out some 'quick hack' ideas :-)
So, after much testing and discussing with my partner-in-crime we found out that instead of using port 21 and taking for example port 22 - the problem goes away.
I have no idea why it behaves like this, but it does...
You post raises questions for me. Like why are you choosing well known ports for this service? I don't believe in coincidences and suspect your use of the term "partner-in-crime" may have more truth then I would care to be associated with.
Then also I am wondering why you assume a Windows bug and not one in the MyNetowrking.dll. Sure, you have been using this for five years. But it still hasn't had the level of vetting that Microsoft gives their code.