I'm writing a bot for moderating my twitch.tv channel in C#.
Here's the basic code for the loop, which is done by a background worker to avoid UI freezes. There's a TCPClient (Client), StreamReader (Reader), StreamWriter (Writer), and NetworkStream (Stream).
private void listener_dowork(object sender, DoWorkEventArgs e)
{
string Data = "";
while ((Data = Reader.ReadLine()) != null)
{
//Perform operations on the received data
}
Console.WriteLine("Loop ended");//this shouldn't happen
}
private void listener_workercompleted(object sender, RunWorkerCompletedEventArgs e)
{
//basically, display a console message that says "OOPS!" and try to reconnect.
}
I get the message "Loop ended" and "OOPS!" and at that point, I get the exception (which I cannot for the life of me catch).
The thing is, I can physically unplug the network cable from my computer wait 30 seconds and plug it back in, and it'll continue just fine.
The full exception is:
System.Net.Sockets.SocketException (0x80004005): An established connection was aborted by the software in your host machine
at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Sockets.NetworkStream.Write(Byte[] buffer, Int32 offset, Int32 size)
Note the lack of a line number, which is present in every other kind of exception I've had, which means I have no idea which part of the program is causing the exception, even though I've put every possible line inside a try/catch.
I guess what I'm looking for is some insight into why this is occurring.
It happens invariably every time I start the bot and leave it running for a few minutes on any channel, though the number of minutes varies.
As I already said in the comments Twitch.tv uses IRC as underlying system for their chat. In order to stay connected with the server you need to reply to "PING" requests that are frequently sent by the server (usually every 30 seconds, may vary depending on the servers implementation). You can read up more about the IRC client protocol in RFC 2812.
You said you already have a StreamWriter and Reader, all you need to do is check if the line contains "PING" and reply with a "PONG":
if (Data.Contains("PING"))
{
_streamWriter.WriteLine(Data.Replace("PING","PONG");
_streamWriter.Flush();
}
Related
I want to send SMS to bulk of users(4000 user) so i put the following method on loop :
protected int SendSMS(string url)
{
// Now to Send Data.
StreamWriter writer = null;
StringBuilder postData = new StringBuilder();
Uri myUri = new Uri(url);
postData.Append(HttpUtility.ParseQueryString(myUri.Query).Get("Username"));
postData.Append(HttpUtility.ParseQueryString(myUri.Query).Get("Password"));
postData.Append(HttpUtility.ParseQueryString(myUri.Query).Get("Sender"));
postData.Append(HttpUtility.ParseQueryString(myUri.Query).Get("Recipients"));
postData.Append(HttpUtility.ParseQueryString(myUri.Query).Get("MessageData"));
string webpageContent = string.Empty;
byte[] byteArray = Encoding.UTF8.GetBytes(postData.ToString());
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = webRequest.ContentLength = byteArray.Length;
writer = new StreamWriter(webRequest.GetRequestStream());
try
{
using (Stream webpageStream = webRequest.GetRequestStream())
{
webpageStream.Write(byteArray, 0, byteArray.Length);
}
using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
{
using (StreamReader reader = new StreamReader(webResponse.GetResponseStream()))
{
webpageContent = reader.ReadToEnd();
//TODO:parse webpagecontent: iF response contains "OK"
if (webpageContent.Contains("OK")) return 1;
else return 0;
}
}
//return 1;
}
catch (Exception ee)
{
ErrMapping.WriteLog(url);
string error = ee.Message + "<br><br>Stack Trace : " + ee.StackTrace;
ErrMapping.WriteLog(error);
return -1;
}
}
After a specific number of users like 65 user, no sms had been sent for the rest of users and
I get the following exception :
Error Message:Thread was being aborted.<br><br>Stack Trace : at System.Net.UnsafeNclNativeMethods.OSSOCK.recv(IntPtr socketHandle, Byte* pinnedBuffer, Int32 len, SocketFlags socketFlags)
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, SocketError& errorCode)
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.PooledStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.Connection.SyncRead(HttpWebRequest request, Boolean userRetrievedStream, Boolean probeRead)
at System.Net.ConnectStream.ProcessWriteCallDone(ConnectionReturnResult returnResult)
at System.Net.HttpWebRequest.CheckDeferredCallDone(ConnectStream stream)
at System.Net.HttpWebRequest.GetResponse()
at SendSMS_EmailUI.Frm_SMS_send.SendSMS(String url)
IMHO, the bulk operations that will be performed on behalf of the application can be done easily by the following procedure
When a bulk SMS is triggered, the details will be entered in a database table
A windows service will be constantly monitoring this table for any updates
When the windows service finds the new entries, it will take few records like few hundreds and then send them. Batch Processing.
There can be a delay between consequent requests.
This will ensure you to track which line items have failed and also does not clog the server with the bulk data.
This is a most widely suggested approach.
Please provide your comments on this implementation.
I have built an SMS portal. What you describe was also experienced in v1.0 of my application. The solution was to have my SMS gateway provide me with Bulk SMS access via HTTP. I could put up to 1000 destinations into an XML (or comma delimited) package and send to the Bulk SMS Gateway. Because I run on a shared host, I limited this to 500 destinations.
I have a cache/temporary storage/table where I batch large destination (up to 1,000,000) in some cases and a scheduler (timer based) sends each batch of 500 every few seconds (by calling a script repeatedly) until the messages are sent. Works like charm!
For personalized messages, I encourage the client to use my desktop application for personalization before forwarding to my SMS portal. Good luck
PROCESS:
You'll need three items
The Script that receives the SendSMS request
The script that sends the SMS
The Scheduler/Timer (Script/Host Service)
A. The Send SMS request arrives with
a. The Message and Sender ID/ Sender GSM Number.
b. The Destinations (as a comma delimited list). We'll assume 10,000 destinations
B. Split the destinations into 500 (any size you wish) and log the 500 destinations along with the message and SenderID in each INBOX row/record.
Note: if you count 500 out by looping (10,000 loops), the script could Time out. GSM Numbers in My country are 13 digits.
So I do a loop Sub String of length 500(13+1) to get 500 destinations per batch (20 loops).*
C. Call the Script that Sends the SMS. Sends the first 500 and tag the message as Sent. You can add Time Sent. Start the Scheduler
D. The Scheduler checks every 1.5 minutes if any unsent messages exist in the INBOX and sends it. If nothing, Scheduler Stops.
So, 10,000 messages are sent within 30 mintues
We do something similar to what saravan suggested for email messages, and suspect it will work SMS.
Basically the service that runs on our web server only sends x at a time, and there is a custom delay y between each send.
It can send two thousand in less than ten minutes with neither a CPU nor a bandwidth spike.
Some tips that weren't in our original design to keep in mind:
Have a way to manually stop all sending (see next tip)
Have a user friendly way to terminate a particular message. If your code (or the user) accidentally sends the same thing five times, you want a way to abort it, quickly.
Use a config file for both numbers (x & y above) so you can adjust without redeploying. I think the y delay is 50 ms.
Before you decide to bundle the same message to multiple recipients, make sure that smartphones can't reply to everyone else in their bundle.
HTH,
-Chris C.
I suspect the problem is that because you are on an asp.net website and this is a long-running task that it is timing out the response.
this timeout is controlled in the web.config and it's default is set to 110 seconds. Increase this to a much longer number and see if it starts working.
<system.web>
<!-- 600 seconds = 10 minute timeout --->
<httpRuntime executionTimeout="600"/>
</system.web>
A better approach would be to using a separate thread and returning updates of the progress
to the user, although this would be more complex, but ultimately more reliable.
See Parallel.ForEach for a simple way to thread this process.
Msdn - httpRuntime documention
This is not answering your original question, but is related useful info. Once you solve this problem, you may hit another -- telcos routinely block bulk-sent SMS messages as a way to suppress SMS spam. If you will be doing this on a commercial scale, you will need to file a brief with the Mobile Marketing Association, and get their approval, which can take a considerable amount of time.
check if your StreamWriter is getting disposed
using (StreamWriter writer = new StreamWriter(webRequest.GetRequestStream()))
{
}
May be the buffer value is full please check it and put it to maximum value in web-config file
Maybe your are making web request too fast: try slowing them down to rule out timing problems, for example adding a sleep:
System.Threading.Thread.Sleep(1000);
1000ms = 1 sec is only a hint, you should try different values to see if something changes
I have a .NET 4 Windows service I've written that periodically (usually once a day) communicates with an external device over a serial port. All in all the service works great, but for one customer, every now and then, a call to SerialPort.Open() throws the following exception:
System.IO.IOException: Insufficient system resources exist to complete the requested service.
at System.IO.Ports.InternalResources.WinIOError(Int32 errorCode, String str)
at System.IO.Ports.SerialStream..ctor(String portName, Int32 baudRate, Parity parity, Int32 dataBits, StopBits stopBits, Int32 readTimeout, Int32 writeTimeout, Handshake handshake, Boolean dtrEnable, Boolean rtsEnable, Boolean discardNull, Byte parityReplace)
at System.IO.Ports.SerialPort.Open()
Based on the exception, would would think that the server is running low on resources, but that doesn't seem to be the case. The CPU is more or less idle and there's plenty of memory and disk.
There are lots of mentions online of SerialPort.Open() throwing other IOExceptions and I have implemented Zach Saw's SerialPortFixer, but it appears it fixes a different issue.
Here's an example of what I'm doing (greatly simplified). A couple of instances of this class (using different serial port names) are in memory at all times and then the Run() method is called approximately once a day for each instance.
public class Collector
{
private SerialPort _port;
private string _portName;
public void Run()
{
try
{
// Run Zach Saw's IOException workaround
SerialPortFixer.Execute(_portName);
using (_port = new SerialPort(_portName, 9600, Parity.None, 8, StopBits.One))
{
_port.DataReceived += PortDataReceived;
_port.ErrorReceived += PortErrorReceived;
_port.Handshake = Handshake.None;
_port.DtrEnable = true;
_port.RtsEnable = true;
_port.Open();
// Do the stuff
_port.Close();
}
}
catch (Exception e)
{
// Handle exception
}
}
private void PortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
// Do other stuff
}
private void PortErrorReceived(object sender, SerialErrorReceivedEventArgs e)
{
// Log error
}
}
Any help would be appreciated.
The answer to this question is that the serial port server used by the client was a wi-fi version (Moxa NPort W2150 Plus) and it turns out the exception occurs when the serial port server has wi-fi connectivity problems.
Had the same problem with NPort 5150. Increasing Network timeout (in NPort Administrator) solved my problem.
I recently had the same problem, the resolution for me was that I was accidentally using a straight-through DB9 cable with a device that required a null modem DB9 cable. Once I swapped to a null modem cable the error disappeared and the device functioned properly.
When I called Moxa, they suggested some other things to try that might lead to this problem:
Anti-virus software could be interfering with the communication
Try using the Driver Manager Utility rather than Nport Administrator when setting up the COM ports (on Windows machines)
I had this error and my problem was that the NPort Administration found and programmed the serial ports but the IP4 Address of the network card was set to DHCP. I think this happened during a system update.
Recovery from that:
Add a TCP/IP address in the subnet of the network interface
Reassign the serial ports in NPort Administration
I have an application that's listening messages from a modem in some 30 cars. I've used TcpListener to implement server code that looks like this (error handling elided):
...
listener.Start()
...
void
BeginAcceptTcpClient()
{
if(listener.Server.IsBound) {
listener.BeginAcceptTcpClient(TcpClientAccepted, null);
}
}
void
TcpClientAccepted(IAsyncResult ar)
{
var buffer = new byte[bufferSize];
BeginAcceptTcpClient();
using(var client = EndAcceptTcpClient(ar)) {
using(var stream = client.GetStream()) {
var count = 0;
while((count = stream.Read(buffer, total, bufferSize - total)) > 0) {
total += count;
}
}
DoSomething(buffer)
}
I get the messages correctly, my problem lies with disconnections. Every 12 hours the modems get reset and get a new IP address, but the Server continues to hold the old connections active (they are marked as ESTABLISHED in tcpview). Is there any way to set a timeout for the old connections? I thought that by closing the TcpClient the TCP connection was closed (and that's what happens in my local tests), what I'm doing wrong?
I'm actually a little confused by the code sample - the question suggests that these questions are open a reasonably long time, but the code is more typical for very short bursts; for a long-running connection, I would expect to see one of the async APIs here, not the sync API.
Sockets that die without trace are very common, especially when distributed with a number of intermediate devices that would all need to spot the shutdown. Wireless networks in particular sometimes try to keep sockets artificially alive, since it is pretty common to briefly lose a wireless connection, as the devices don't want that to kill every connection every time.
As such, it is pretty common to implement some kind of heartbeat on connections, so that you can keep track of who is still really alive.
As an example - I have a websocket server here, which in theory handles both graceful shutdowns (via a particular sequence that indicates closure), and ungraceful socket closure (unexpectedly terminating the connection) - but of the 19k connections I've seen in the last hour or so, 70 have died without hitting either of those. So instead, I track activity against a (slow) heartbeat, and kill them if they fail to respond after too long.
Re timeout; you can try the ReceiveTimeout, but that will only help you if you aren't usually expecting big gaps in traffic.
I'm learning netcode and multithreading in Monodevelop, using C# with GTK#. I've never done either before, and now I find myself needing to do both at once.
I've used a tutorial chat program that has no error handling, and I've caught an error that happens in the client every single time I disconnect from the server. The code that sits in a thread listening for messages is as follows, surrounded by try/catch statements:
try
{
while (Connected)
{
if (!srReceiver.EndOfStream && Connected)
{
string temp = srReceiver.ReadLine();
// Show the messages in the log TextBox
Gtk.Application.Invoke(delegate
{
UpdateLog(temp);
});
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
After which the function finishes and the thread ends.
The code that ends the connection looks like this, and runs on the main thread:
private void CloseConnection(string Reason)
{
// Show the reason why the connection is ending
UpdateLog(Reason);
// Enable and disable the appropriate controls on the form
txtIp.Sensitive = true;
txtUser.Sensitive = true;
txtMessage.Sensitive = false;
btnSend.Sensitive = false;
btnConnect.Label = "Connect";
// Close the objects
Connected = false;
swSender.Close();
srReceiver.Close();
tcpServer.Close();
}
And the try/catch statements above catch this error:
System.IO.IOException: Unable to read data from the transport
connection: A blocking operation was interrupted by a call to
WSACancelBlockingCall. ---> System.Net.Sockets.SocketException: A
blocking operation was interrupted by a call to WSACancelBlockingCall
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset,
Int32 size, SocketFlags socketFlags)
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32
offset, Int32 size)
--- End of inner exception stack trace ---
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32
offset, Int32 size)
at System.IO.StreamReader.ReadBuffer()
at System.IO.StreamReader.get_EndOfStream()
at ChatClientGTK.MainWindow.ReceiveMessages() in
g:\Android\Tutes\ChatClientRemake\ChatClientGTK\MainWindow.cs:line 157
Now, as far as I can tell, when srReciever.Close() happens in the main thread, srReciever.ReadLine() is still trying to execute in the listening thread, which is where the problem lies, but even when I comment out srReciever.Close(), I still get the error.
As far as I can tell, there are no side-effects caused by just catching the error and moving on, but that doesn't really sit right with me. Do I need to fix this error, and if so, does anyone have any ideas?
Instead of using a ReadLine, can't you just do a Read and build up the String until a CrLf is detected then output that to update log.
ReadLine is a blocking call meaning it will sit there and always error if the connection is closed.
Otherwise you could just ignore the error. I know what you mean when you say it doesn't sit right but unless anyone else can enlighten me, I don't see that there is any leak in resources due to it and if it is an expected error then you can handle it appropriately.
Also I would probably catch the specific exception
catch (IOException ex)
{
Console.WriteLine(ex.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
The error is fine. If you really want it to go away, you can include a "bye" command in your protocol. So, if the server decides to disconnect, right before disconnecting he sends a "bye" to the client, so the client disconnects too, and most chances are that the exception will not be thrown. But you should still be prepared to catch it if it ever gets thrown. And then ignore it.
I have a TCP/IP connection between a server and a client. Once the connection is established, the server will start sending a lot of data to the client. The problem that I'm having is if the connection is slow, at some point the server will become unresponsive. I guess this is because it's buffering data waiting for the data that has already been sent to be acknowledged.
Is there are way before trying to send data to check the size of the current buffer, so I know I should wait before continuing sending data?
Thanks.
This is what I'm doing to send data:
System.Net.Sockets.Socket ClientSocket;
...
public void Send(byte[] data, int size)
{
try
{
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.SetBuffer(data, 0, size);
bool pending = ClientSocket.SendAsync(e);
}
catch (Exception ex)
{
}
}
The documentation for SendAsync says that it will raise the SocketAsyncEventArgs.Completed event on the e parameter when the operation is complete. So if you write an event handler that responds to that event, you can always know how many operations you have outstanding.
There's a reasonably good example of using the event here: http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx
I found the solution. Basically I should only send data when
ClientSocket.Poll(0, SelectMode.SelectWrite) is true.
Easy and effective!