My code working perfectly if the SMS messages to be sent has less than 70 characters.
I want to send messages with between 70 to 200 characters per message.
using GsmComm.GsmCommunication;
using GsmComm.PduConverter;
using GsmComm.Server;
using GsmComm.PduConverter.SmartMessaging;
namespace SMSSender
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
string msg = " کو ہم نے";
GsmCommMain comm = new GsmCommMain(4, 19200, 500);
comm.Open();
SmsSubmitPdu pdu;
pdu = new SmsSubmitPdu(msg, "03319310077", DataCodingScheme.NoClass_16Bit);
comm.SendMessage(pdu);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
If you take a look at the source here for the SmsPdu class here, you can see that there's an explicit limit of 70 Unicode characters, which would explain the issue you are encountering:
public abstract class SmsPdu : ITimestamp
{
// Omitted for brevity
/// <summary>
/// Gets the maximum Unicode message text length in characters.
/// </summary>
public const int MaxUnicodeTextLength = 70;
// Omitted for brevity
}
Possible Workaround
A possible workaround might involve breaking a single message into multiple batches of less than 70 characters and sending multiple to the same destination:
public static IEnumerable<string> BatchMessage(string message, int batchSize = 70)
{
if (string.IsNullOrEmpty(message))
{
// Message is null or empty, handle accordingly
}
if (batchSize < message.Length)
{
// Batch is smaller than message, handle accordingly
}
for (var i = 0; i < message.Length; i += batchSize)
{
yield return message.Substring(i, Math.Min(batchSize, message.Length - i));
}
}
And then simply call that prior to sending your message and send the batches individually:
// Open your connection
GsmCommMain comm = new GsmCommMain(4, 19200, 500);
comm.Open();
// Store your destination
var destination = "03319310077";
// Batch your message into one or more
var messages = BatchMessage(" کو ہم نے");
foreach (var message in messages)
{
// Send each one
var sms = new SmsSubmitPdu(message, destination, DataCodingScheme.NoClass_16Bit);
comm.SendMessage(sms);
}
Related
I'm creating a program which communicates with a serial device which is constantly sending data. I'm reading data from device every 100ms (using a timer). I use port.ReadExisting() to receive all currently available data from the device then I try split it into lines, because I need to check some of the received data and the best way is to check lines. The problem occurs when device sends data which doesn't end with "\r\n" or '\n'.
In a perfect situation port.ReadExisting() returns: "sampletext\r\nsomesampletext\nsampletext\r\n
But a problem occurs when there's no CR or LF character at the end:
First time port.ReadExisting() returns this: "text\nsamp"
Second time port.ReadExisting() returns this: letext\r\ntext\r\n"
End result should look like this:
text
sampletext
text
But what I get looks like this:
text
samp
letext
text
My code:
This is the timer which runs every 100ms:
private void CommandTimer_Tick(object sender, EventArgs e)
{
BackgroundWorker seriaDataWorker = new BackgroundWorker();
seriaDataWorker.DoWork += (obj, p) => PrintSerialData();
seriaDataWorker.RunWorkerAsync();
}
BackgroundWorker which gets called by the timer:
private void PrintSerialData()
{
try
{
if (RandomReboot)
{
RebootWatch.Start();
}
if (COMport.IsOpen)
{
if (COMport.BytesToRead != 0)
{
SerialPrint(COMport.ReadExisting());
}
}
}
catch (System.IO.IOException SerialException)
{
return;
}
}
Function which parses received data into lines:
private void SerialPrint(string data)
{
using (var buffer = new StringReader(data))
{
string line = "";
while((line = buffer.ReadLine()) != null)
{
if (CheckForAnsw)
{
ReceivedCommandData = line;
if (ReceivedCommandData.Contains(AnswExpected))
{
ReceivedAnsw = true;
ReceivedLine = ReceivedCommandData;
ReceivedCommandData = "";
}
}
this.Invoke(new MethodInvoker(delegate
{
AppendText(TextBox_System_Log, Color.Black, line + "\r\n");
}
));
}
}
}
I know that the problem is that buffer.ReadLine() treats remainder of the string which doesn't end with a CR or LF character as a seperate line but I don't know how to fix it.
I tried using port.ReadLine() in the past but it is way slower and causes problems for me when serial ports get disconnected etc.
I don't think there's an easy way to handle this with the StringReader. Instead, you can split the string yourself:
private static string _buffer = string.Empty;
private static void SerialPrint(string data)
{
// Append the new data to the leftover of the previous operation
data = _buffer + data;
int index = data.IndexOf('\n');
int start = 0;
while (index != -1)
{
var command = data.Substring(start, index - start);
ProcessCommand(command.TrimEnd('\r'));
start = index + 1;
index = data.IndexOf('\n', start);
}
// Store the leftover in the buffer
if (!data.EndsWith("\n"))
{
_buffer = data.Substring(start);
}
else
{
_buffer = string.Empty;
}
}
private static void ProcessCommand(string command)
{
Console.WriteLine(command);
}
You can use AnonymousPipes to transport and buffer the incoming data and read them as lines to output them to somewhere.
Here is a little example which creates a server and client pipe stream, then writes data to the server in one task (with some newline in the data) and reads the data in a different task per line and outputs them to the console.
public class Program
{
public static async Task Main()
{
(var writer, var reader) = CreatePipe();
using (writer)
using (reader)
{
var writerTask = Task.Run(async () =>
{
writer.AutoFlush = true;
writer.Write("?");
for (int i = 0; i < 100; i++)
{
if (i % 10 == 9)
{
await writer.WriteAsync("!");
await writer.WriteAsync(Environment.NewLine);
await writer.WriteAsync("?");
}
else
{
await writer.WriteAsync((i % 10).ToString());
}
await Task.Delay(100);
}
writer.Close();
});
var readerTask = Task.Run(async () =>
{
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
Console.WriteLine(line);
}
});
await Task.WhenAll(writerTask, readerTask);
}
}
public static (StreamWriter, StreamReader) CreatePipe()
{
var server = new AnonymousPipeServerStream(PipeDirection.Out);
var client = new AnonymousPipeClientStream(server.GetClientHandleAsString());
return
(
new StreamWriter(server, Encoding.UTF8),
new StreamReader(client, Encoding.UTF8)
);
}
}
Try to adapt this code to your use case and comment if there are difficulies.
Your issue with \r\n and \n can be covered by using Environment.NewLine. I'm not sure what AppendText does, but if you're using it to store the values, then you're overdoing it. What you need is to store all data first in a StringBuilder then process them, OR process each data and store them in managed type such as Array, to define each line separately. Only use the string in the presentation layer (if you have some GUI that you want the user to see the results).
So, what I suggest is to store the lines in StringBuilder Something like this :
private readonly StringBuilder _strDataBuilder = new StringBuilder();
private void PrintSerialData()
{
try
{
if (RandomReboot)
{
RebootWatch.Start();
}
if(COMport.IsOpen && COMport.BytesToRead != 0)
{
var data = COMport.ReadExisting();
if(!string.IsNullOrEmpty(data)) {
_strDataBuilder.Append(data);
}
}
}
catch (System.IO.IOException SerialException)
{
return;
}
}
private void SerialPrint()
{
var data = _strDataBuilder.ToString();
if(string.IsNullOrEmpty(data)) { return; }
var lines = data.Split(Environment.NewLine);
if(lines.Length == 0) { return; }
for(int x = 0; x < lines.Length; x++)
{
var line = lines[x];
if (CheckForAnsw)
{
ReceivedCommandData = line;
if (ReceivedCommandData.Contains(AnswExpected))
{
ReceivedAnsw = true;
ReceivedLine = ReceivedCommandData;
ReceivedCommandData = "";
}
}
this.Invoke(new MethodInvoker(delegate
{
AppendText(TextBox_System_Log, Color.Black, line + Environment.NewLine);
}
));
}
}
Storing them first would make things more maintainability and fixability when you want to add more processing steps or reuse the results.
Although the SerialPrint() is unnessary if you just re-print the data in the GUI. As the data already separated in lines. So, if you do
TextBox_System_Log.Text = _strDataBuilder.ToString();
Directly, would list them in lines in the default color. However, if you intended to split them to process each line separately (to validate for instance), then it would be okay.
You can try like below code:
public void DataReceivedSerialPort(object sender, SerialDataReceivedEventArgs e)
{
readExistingData = "";
SerialPort sp = (SerialPort)sender;
sp.ReadTimeout = 100;
do
{
readExistingData = "";
try
{
readExistingData = sp.ReadLine();
if (readExistingData == "")
{
readExistingData = sp.ReadLine();
}
dataReadFromSerialPort += readExistingData;
}
catch
{
try
{
readExistingData = sp.ReadExisting();
dataReadFromSerialPort += readExistingData + "\r\n";
}
catch { }
}
UI.insert_new_items_into_textBoxUARTLog(readExistingData);
} while (readExistingData != "");
}
I'm searching for a Streamclass which contains:
- a method for sending/receiving a byte-array
- a method for sending/receiving a string
The only Class I've found was NetworkStream. But the disadvantage with the NetworkStream-Class is, that if i want sending a string, i must befor convert this string into a byte-array and send this byte-array, because there is no method for sending strings directly.
And on the other side classes like Streamwriter have methods for sending/receiving strings, but there have no methods for sending/receiving a byte-array.
And if i try to combine these two Streamclasses like this:
TcpClient clientConnection = new TcpClient();
NetworkStream nws = clientConnection.GetStream();
StreamWriter sw = new StreamWriter(nws);
sw.writeLine("ABC");
sw.Flush();
nws.Write(byteArray, 0, lengthToSend);
i get a lot of strange errors (like byteArray will not receive on the other side completly), because i'm using here the same one stream in two different ways.
So, must i used NetworkStream-Class for my plan or exists there a better way?
I had the same problem,and the point is that the other side doesnt know what you are sending byte array or string so what i did is putting a header for each msg specially when dealing with serious server/client application coz you will have multiple data (user info, requesting info,replying info .. etc)
i am using streamwriter to send and streamreader to receive but i am also using threads
the connection remains open as long as the client is connected so i declare them once
here is a full example of my codes
public class Client
{
private StreamWriter swSender;
private StreamReader srReceiver;
private TcpClient tcpServer;
private Thread thrMessaging;
private string UserName = "UK";
private byte Tries = 0;
private bool Connected = false;
public void Connect()
{
if (!Connected)
{
IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName());
string User = localIPs[0].ToString();
string ServIP = "127.0.0.1";//change this to your server ip
InitializeConnection(ServIP, User);
}
else
{
CloseConnection("Disconnected at user's request.");
}
}
private void InitializeConnection(string ServIp, string User)
{
IPAddress ipAddr = IPAddress.Parse(ServIp);
tcpServer = new TcpClient();
try
{
tcpServer.Connect(ipAddr, 1986);//change that 1986 to your server port
}
catch
{
if (Connected) CloseConnection("");
MessageBox.Show("Connecteing to " + ServIp + "\r\nServer is Down ... Try nomber " + Tries); return;
}
Connected = true;
UserName = User;
swSender = new StreamWriter(tcpServer.GetStream());
swSender.WriteLine(User);
swSender.Flush();
thrMessaging = new Thread(new ThreadStart(ReceiveMessages));
thrMessaging.Start();
}
private void ReceiveMessages()
{
srReceiver = new StreamReader(tcpServer.GetStream());
string ConResponse = srReceiver.ReadLine();
if (ConResponse[0] == '1')
{
}
else
{
string Reason = "Not Connected: ";
Reason += ConResponse.Substring(2, ConResponse.Length - 2);
return;
}
while (Connected)
{
try
{
string NewMsg = srReceiver.ReadLine();
if (NewMsg != null && NewMsg != "")
PacketHandler.HandlePacket(NewMsg, this);
}
catch { }
}
}
public void CloseConnection(string Reason)
{
try
{
Connected = false;
swSender.Close();
srReceiver.Close();
tcpServer.Close();
}
catch { }
}
public void SendMessage(string Msg)
{
if (Msg.Length >= 1)
{
try
{
Tries++;
swSender.WriteLine(Msg);
swSender.Flush();
Tries = 0;
}
catch
{
if (Tries < 5)
{
try
{
CloseConnection("No connection made");
Connect();
}
catch { }
SendMessage(Msg);
}
else { MessageBox.Show("Connecting to server faild for 5 tries"); Tries = 0; }
}
}
}
then at the packet handler i do my handling to check what kind of data the client received
something like this
public static void HandlePacket(string MsgRec, Client Client)
{
string[] Info = MsgRec.Split('|');
string Type = Info[0];
if (Type == "")
{
return;
}
string subtype = Info[1];
int TLen = Type.Length + subtype.Length + 2;
string Data = MsgRec.Remove(0, TLen);//this is the main data the server sent
ushort PacketType = ushort.Parse(Type);
ushort SubType = ushort.Parse(subtype);
switch ((Structs.PacketType)PacketType)
{
case Structs.PacketType.Login:
{
//do your stuff here
break
}
case Structs.PacketType.Image:
{
//convert the Data back to byte array then get the image out from it
break
}
case Structs.PacketType.ByteArray:
{
//convert the Data back to byte array
break
}
}
}
i know its kinda messy and not the perfect way to do it , but it works for me
and remember that at the other side when sending something you need to add the packet type and subtype , or just any header with any splitter if u doin something simple
Finally : i think using Sockets and packets would be much easier if u are sending small packets length
I dont know if this is a good way to work with a stack for this task but I'm sure there is a faster way ...
I get data from my microcontroller but the data length is not always the same length.
I thought maybe I can push data in my stack and in a thread I can pop it and decode the message. I didnt wanted slow down the DataReceivedHandler so then I created a Thread which can pop the data and write it to my Listview in my decodeMessage() function.
After a short time I get a System.OutOfMemories Exception..
Any ideas how I can do it in a better way ?
I'm reading from my serial port just when data arrives here:
Stack<byte[]> stack = new Stack<byte[]>();
.....
public void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
byte[] data = new byte[sp.BytesToRead];
sp.Read(data, 0, data.Length);
stack.Push(data);
}
And this is my Thread:
private void formatData()
{
try
{
while (true)
{
byte[] data;
int i=0;
Dispatcher.BeginInvoke(new Action(() =>
{
while (stack.Count > 0)
{
data = stack.Pop();
while (i < data.Length)
{
decodeMessage(data[i]);
i++;
}
}
}));
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
thx
This code use a thread safe queue. I simplified some of my own code, so this code is not tested or compiled. If you have problems compiling or it produce errors, add a comment to me and I will help you out.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Windows.Threading;
using System.Collections.Concurrent;
void someRoutine()
{
// initialize queue before using it
serialDataQueue = new ConcurrentQueue<char>();
}
/// <summary>
/// data from serialPort is added to the queue as individual chars,
/// a struct may be better
/// </summary>
public ConcurrentQueue<char> serialDataQueue;
// get data
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = sender as SerialPort;
int bytesAvailable = sp.BytesToRead;
// array to store the available data
char[] recBuf = new char[bytesAvailable];
try
{
// get the data
sp.Read(recBuf, 0, bytesAvailable);
// put data, char by char into a threadsafe FIFO queue
// a better aproach maybe is putting the data in a struct and enque the struct
for (int index = 0; index < bytesAvailable; index++)
serialDataQueue.Enqueue(recBuf[index]);
}
catch (TimeoutException ex)
{
// handle exeption here
}
}
/// <summary>
/// Check queue that contains serial data, call this
/// routine at intervals using a timer or button click
/// or raise an event when data is received
/// </summary>
private void readSearialDataQueue()
{
char ch;
try
{
while (serialDataQueue.TryDequeue(out ch))
{
// do something with ch, add it to a textbox
// for example to see that it actually works
textboxDataReceived.Text += ch;
}
}
catch (Exception ex)
{
// handle ex here
}
}
I am having the following issue:
Once I close my WM6 application and then try to start it again i get this error:
Only one usage of each socket address (protocol/network address/port) is normally permitted at System.Net.Sockets.Socket.Bind(EndPoint localEP)
at
System.Net.Sockets.Socket.TcpListener.Start()
...
I think this is due to the time interval for the connection to timeout, so I would like to close all open conections and force it to create a new connection, is this the correct way to proceed or is there a different way to handle this?
Here is the code used to start listening:
/// <summary>
/// Listens Asynchronously to Clients, creates a recieveMessageHandler to process the read.
///
/// Check WIKI, TODOS
/// </summary>
/// <returns></returns>
public void Listen()
{
myTcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
try
{
TcpClient myTcpClient = myTcpListener.AcceptTcpClient();
DateTime now = DateTime.Now;
//Test if it's necessary to create a client
ClientConnection client = new ClientConnection(myTcpClient, new byte[myTcpClient.ReceiveBufferSize]);
// Capture the specific client and pass it to the receive handler
client.NetworkStream.BeginRead(client.Data, 0, myTcpClient.ReceiveBufferSize, r => receiveMessageHandler(r, client), null);
}
catch (Exception excp)
{
Debug.WriteLine(excp.ToString());
}
}
}
Yes, your server socket is likely in the TIME_WAIT state.
You can access the underlying ServerSocket and then use SetSocketOption and specify ReuseAddress.
I'm going to guess here that ClientConnection is your DLL, because I don't see that already included in the CF.
You don't really need that, though, if you declare MethodInvoker.
public delegate void MethodInvoker(); // required
To make your code really slick, you should also create your very own EventArgs class:
public class WmTcpEventArgs : EventArgs {
private string data;
public WmTcpEventArgs(string text) {
data = text;
}
public string Data { get { return data; } }
}
Very simple. With this new WmTcpEventArgs class and, you should be all set to receive your data that could post to something like a TextBox control:
private void NetworkResponder(object sender, WmTcpEventArgs e) {
textBox1.Text = e.Data;
}
Instead of coding a while(true) in your code, I prefer to include a little Boolean variable
private bool abortListener;
The code would look something like this:
public void Listen() {
listener.Start();
while (!abortListener) {
try {
using (var client = listener.AcceptTcpClient()) {
int MAX = client.ReceiveBufferSize;
var now = DateTime.Now;
using (var stream = client.GetStream()) {
Byte[] buffer = new Byte[MAX];
int len = stream.Read(buffer, 0, MAX);
if (0 < len) {
string data = Encoding.UTF8.GetString(buffer, 0, len);
MethodInvoker method = delegate { NetworkResponder(this, new WmTcpEventArgs(data)); };
abortListener = ((form1 == null) || form1.IsDisposed);
if (!abortListener) {
form1.Invoke(method);
}
}
}
}
} catch (Exception err) {
Debug.WriteLine(err.Message);
} finally {
listener.Stop();
}
}
}
Notice you are still catching your Exceptions, but you also stop the TcpListener.
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.