0

I have been following an old tutorial for making a chat program and I have been dissecting it to fit into a new form, although I have gotten it to work as it was intended it runs into the error: "Unable to read data from the transport connection: A blocking operation was interrupted by a call to WSACancelBlockingCall."

that points to this part of the code.

        while (Connected)
        {
            // Show the messages in the log TextBox
            this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { srReceiver.ReadLine() });
        }

I only get the error upon closing the client, or disconnecting.

this is the majority of the client code.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;

namespace Table_Top_RPG
{
    public partial class Connect : Form
    {
        // Will hold the user name
        private string UserName = "Unknown";
        public static StreamWriter swSender;
        private StreamReader srReceiver;
        private TcpClient tcpServer;
        // Needed to update the form with messages from another thread
        private delegate void UpdateLogCallback(string strMessage);
        // Needed to set the form to a "disconnected" state from another thread
        private delegate void CloseConnectionCallback(string strReason);
        private Thread thrMessaging;
        private IPAddress ipAddr;
        private bool Connected;

        public Connect()
        {
            // On application exit, don't forget to disconnect first
            Application.ApplicationExit += new EventHandler(OnApplicationExit);
            InitializeComponent();
        }

        private void BtnConnect_Click(object sender, EventArgs e)
        {
            // If we are not currently connected but awaiting to connect
            if (Connected == false)
            {
                InitializeConnection();

            }
            else // We are connected, thus disconnect
            {
                CloseConnection("Disconnected at user's request.");
            }
        }

        // The event handler for application exit
        public void OnApplicationExit(object sender, EventArgs e)
        {
            if (Connected == true)
            {
                // Closes the connections, streams, etc.
                Connected = false;
                swSender.Close();
                srReceiver.Close();
                tcpServer.Close();
            }
        }

        private void InitializeConnection()
        {
            // Parse the IP address from the TextBox into an IPAddress object
            ipAddr = IPAddress.Parse(Connect.IpBox.Text);
            // Start a new TCP connections to the chat server
            tcpServer = new TcpClient();
            tcpServer.Connect(ipAddr, int.Parse(Connect.PortBox.Text));
            // Helps us track whether we're connected or not
            Connected = true;
            // Prepare the form
            UserName = Connect.NameBox.Text;
            // Disable and enable the appropriate fields
            IpBox.Enabled = false;
            NameBox.Enabled = false;
            Main.TxtMsg.Enabled = true;
            Connect.BtnConnect.Text = "Disconnect";
            // Send the desired username to the server
            swSender = new StreamWriter(tcpServer.GetStream());
            swSender.WriteLine(UserName);
            swSender.Flush();
            // Start the thread for receiving messages and further communication
            thrMessaging = new Thread(new ThreadStart(ReceiveMessages));
            thrMessaging.Start();
        }

        private void ReceiveMessages()
        {
            // Receive the response from the server
            srReceiver = new StreamReader(tcpServer.GetStream());
            // If the first character of the response is 1, connection was successful
            string ConResponse = srReceiver.ReadLine();
            // If the first character is a 1, connection was successful
            if (ConResponse[0] == '1')
            {
                // Update the form to tell it we are now connected
                this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { "Connected Successfully!" });
            }
            else // If the first character is not a 1 (probably a 0), the connection was unsuccessful
            {
                string Reason = "Not Connected: ";
                // Extract the reason out of the response message. The reason starts at the 3rd character
                Reason += ConResponse.Substring(2, ConResponse.Length - 2);
                // Update the form with the reason why we couldn't connect
                this.Invoke(new CloseConnectionCallback(this.CloseConnection), new object[] { Reason });
                // Exit the method
                return;
                }
                // While we are successfully connected, read incoming lines from the server
                while (Connected)
                {
                    // Show the messages in the log TextBox
                    this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { srReceiver.ReadLine() });
                }
            }

            internal void CloseConnection(string Reason)
            {
                // Show the reason why the connection is ending
                Main.ChatLog.AppendText(Reason + "\r\n");
                // Enable and disable the appropriate controls on the form
                IpBox.Enabled = true;
                NameBox.Enabled = true;
                Main.TxtMsg.Enabled = false;
                BtnConnect.Text = "Connect";
                // Close the objects
                Connected = false;
                swSender.Close();
                srReceiver.Close();
                tcpServer.Close();
            }

            // This method is called from a different thread in order to update the log TextBox
            private void UpdateLog(string strMessage)
            {
                // Append text also scrolls the TextBox to the bottom each time
                Main.ChatLog.AppendText(strMessage + "\r\n");
            }
        }
    }

there is another form called Main where all the chat dialog is sent, but the majority of its code is not relevant.

if anyone knows a better way to handle this or knows of a good chat program tutorial i can go through for better examples of how clients connect and disconnect is handled properly without crashing I would be much appreciative.

  • 1
    My eyes hurt just by seeing that you placed all this code in the FORM. – Federico Berasategui Jan 29 '13 at 18:32
  • hahaha, forgive me; but I wasn't sure what would be relevant to coders as to where to problem is occurring and how to fix it. also this was how the tutorial was written so my knowledge of a better way to do it is limited to what I've got. – VincEclipsE Jan 29 '13 at 18:35
  • I think I didn't make myself clear. There's a thing in software development called Separation of Concerns. All this TCP related code does not belong into a `System.Windows.Forms.Form`. – Federico Berasategui Jan 29 '13 at 18:37
  • ahh, yes. I see what you are saying now. I will have to learn how to make my programs more modular, but thanks for pointing that out. – VincEclipsE Jan 29 '13 at 18:47
  • obviously, you are trying to read from a closed stream. Or rather, you are making a blocking read and the stream itself closes, throwing an error because it is currently blocked from reading. Better management of the Stream Closing and Reading code would work. Also, include more try catches and catch the specific case of `WSACancelBlockingCall`. Then you can handle it separately from other errors. – Nevyn Jan 29 '13 at 18:57

2 Answers2

0

You should probably think about using Asynchronous programming, where there are no blocking calls. The problem is, as you surely know, that you close your client while there is an active call.

I'm pretty sure NetworkStream and StreamReader/Writer does have some asynchronous methods. Try to look at some here: http://msdn.microsoft.com/en-us/library/system.io.streamreader.readasync.aspx

Janman
  • 698
  • 1
  • 9
  • 25
0

I believe you need to close and dispose each stream per use in your case because you are performing them synchronously. Consider using the using statement for writing...and do something similar for reading. Plus don't forget to remove them from the CloseConnection...

using (NetworkStream ns=tcpServer.GetStream())
{
    swSender = new StreamWriter(ns);
    swSender.WriteLine(UserName);
    swSender.Flush();
    ns.Close();
    ns.Dispose();
    swSender = null;
}
andrew
  • 91
  • 3