Как исправить ошибку при отключении клиента от сервера - C#
Формулировка задачи:
Доброго времени суток гос-да программисты.
Есть примеры кода клиент-сервер.
Всё работает, но при отключении клиента ("Стоп") или даже просто при закрытии окна чата,
выходит ошибка (скрин прикреплён).
После трёх дней безуспешных попыток исправить самостоятельно, решил создать эту тему,
а вдруг есть на свете такие люди, которые умеют исправлять такое
клиент :
сервер :
Очень прошу помощи в решении этой проблемы.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Net; using System.Net.Sockets; using System.IO; using System.Threading; namespace ChatClient { public partial class Form1 : Form { // Будет хранить имя пользователя private string UserName = "Unknown"; private StreamWriter swSender; private StreamReader srReceiver; private TcpClient tcpServer; // Необходимо обновить форму с сообщениями из другого потокаd private delegate void UpdateLogCallback(string strMessage); // Необходимо установить форму в "отключенном" состоянии из другого потока private delegate void CloseConnectionCallback(string strReason); private Thread thrMessaging; private IPAddress ipAddr; private bool Connected; public Form1() { // На выходе из приложения, не забудьте отключить первый Application.ApplicationExit += new EventHandler(OnApplicationExit); InitializeComponent(); } // Обработчик события для выхода из приложения public void OnApplicationExit(object sender, EventArgs e) { if (Connected == true) { // Закрывает соединения, потоки, и т.д. Connected = false; swSender.Close(); srReceiver.Close(); tcpServer.Close(); } } private void btnConnect_Click(object sender, EventArgs e) { // Если мы сейчас не подключены, но в ожидании подключения if (Connected == false) { // Инициализация связи InitializeConnection(); } else // Мы связаны, таким образом, отсоедините { CloseConnection("Disconnected at user's request."); } } private void InitializeConnection() { // Разбор IP-адреса из TextBox в IP-адрес объекта ipAddr = IPAddress.Parse(txtIp.Text); // Начало нового соединения TCP для чат-сервера tcpServer = new TcpClient(); tcpServer.Connect(ipAddr, 1986); // Помогает нам отслеживать ли мы связаны или нет Connected = true; // Prepare the form UserName = txtUser.Text; // Отключить и включить в соответствующие поля txtIp.Enabled = false; txtUser.Enabled = false; txtMessage.Enabled = true; btnSend.Enabled = true; btnConnect.Text = "Стоп"; // Отправить желаемое имя пользователя на сервере swSender = new StreamWriter(tcpServer.GetStream()); swSender.WriteLine(txtUser.Text); swSender.Flush(); // Начало нити для получения сообщений и дальнейшее общение thrMessaging = new Thread(new ThreadStart(ReceiveMessages)); thrMessaging.Start(); } private void ReceiveMessages() { // Получить ответ от сервера srReceiver = new StreamReader(tcpServer.GetStream()); // Если первый символ в ответ равен 1, соединение было успешным string ConResponse = srReceiver.ReadLine(); // Если первый символ равен 1, соединение было успешным if (ConResponse[0] == '1') { // Обновите форму, чтобы сказать ему мы теперь связаны this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { "Connected Successfully!" }); } else // Если первый символ не является 1 (возможно, 0), связь была неудачной { string Reason = "Not Connected: "; // Извлечение причине из ответного сообщения.Причина начинается с 3-го символа Reason += ConResponse.Substring(2, ConResponse.Length - 2); // Обновление формы с причиной, почему мы не могли соединиться this.Invoke(new CloseConnectionCallback(this.CloseConnection), new object[] { Reason }); // Выход из метода return; } // Пока мы успешно подключились, читать входящие линии с сервера while (Connected) { // Показать сообщения в журнале TextBox this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { srReceiver.ReadLine() }); } } // Этот метод вызывается из другого потока, чтобы обновить журнал TextBox private void UpdateLog(string strMessage) { // Добавить текст также свитки TextBox на дно каждый раз txtLog.AppendText(strMessage + "\r\n"); } // Закрывает текущее соединение private void CloseConnection(string Reason) { // Показать причине соединение заканчивается txtLog.AppendText(Reason + "\r\n"); // Включить и отключить соответствующие элементы управления на форму txtIp.Enabled = true; txtUser.Enabled = true; txtMessage.Enabled = false; btnSend.Enabled = false; btnConnect.Text = "Connect"; // Закройте объектов Connected = false; swSender.Close(); srReceiver.Close(); tcpServer.Close(); } // Отправка сообщения набрали на сервер private void SendMessage() { if (txtMessage.Lines.Length >= 1) { swSender.WriteLine(txtMessage.Text); swSender.Flush(); txtMessage.Lines = null; } txtMessage.Text = ""; } // Мы хотим, чтобы отправить сообщение, когда Send нажатии кнопки private void btnSend_Click(object sender, EventArgs e) { SendMessage(); } // Но мы также хотим, чтобы отправить сообщение один раз Enter нажат private void txtMessage_KeyPress(object sender, KeyPressEventArgs e) { // Если клавиша Enter if (e.KeyChar == (char)13) { SendMessage(); } } } }
using System; using System.Collections.Generic; using System.Text; using System.Net; using System.Net.Sockets; using System.IO; using System.Threading; using System.Collections; namespace ChatServer { // Holds the arguments for the StatusChanged event public class StatusChangedEventArgs : EventArgs { // The argument we're interested in is a message describing the event private string EventMsg; // Property for retrieving and setting the event message public string EventMessage { get { return EventMsg; } set { EventMsg = value; } } // Constructor for setting the event message public StatusChangedEventArgs(string strEventMsg) { EventMsg = strEventMsg; } } // This delegate is needed to specify the parameters we're passing with our event public delegate void StatusChangedEventHandler(object sender, StatusChangedEventArgs e); class ChatServer { // This hash table stores users and connections (browsable by user) public static Hashtable htUsers = new Hashtable(30); // 30 users at one time limit // This hash table stores connections and users (browsable by connection) public static Hashtable htConnections = new Hashtable(30); // 30 users at one time limit // Will store the IP address passed to it private IPAddress ipAddress; private TcpClient tcpClient; // The event and its argument will notify the form when a user has connected, disconnected, send message, etc. public static event StatusChangedEventHandler StatusChanged; private static StatusChangedEventArgs e; // The constructor sets the IP address to the one retrieved by the instantiating object public ChatServer(IPAddress address) { ipAddress = address; } // The thread that will hold the connection listener private Thread thrListener; // The TCP object that listens for connections private TcpListener tlsClient; // Will tell the while loop to keep monitoring for connections bool ServRunning = false; // Add the user to the hash tables public static void AddUser(TcpClient tcpUser, string strUsername) { // First add the username and associated connection to both hash tables ChatServer.htUsers.Add(strUsername, tcpUser); ChatServer.htConnections.Add(tcpUser, strUsername); // Tell of the new connection to all other users and to the server form SendAdminMessage(htConnections[tcpUser] + " has joined us"); } // Remove the user from the hash tables public static void RemoveUser(TcpClient tcpUser) { // If the user is there if (htConnections[tcpUser] != null) { // First show the information and tell the other users about the disconnection SendAdminMessage(htConnections[tcpUser] + " has left us"); // Remove the user from the hash table ChatServer.htUsers.Remove(ChatServer.htConnections[tcpUser]); ChatServer.htConnections.Remove(tcpUser); } } // This is called when we want to raise the StatusChanged event public static void OnStatusChanged(StatusChangedEventArgs e) { StatusChangedEventHandler statusHandler = StatusChanged; if (statusHandler != null) { // Invoke the delegate statusHandler(null, e); } } // Send administrative messages public static void SendAdminMessage(string Message) { StreamWriter swSenderSender; // First of all, show in our application who says what e = new StatusChangedEventArgs("Administrator: " + Message); OnStatusChanged(e); // Create an array of TCP clients, the size of the number of users we have TcpClient[] tcpClients = new TcpClient[ChatServer.htUsers.Count]; // Copy the TcpClient objects into the array ChatServer.htUsers.Values.CopyTo(tcpClients, 0); // Loop through the list of TCP clients for (int i = 0; i < tcpClients.Length; i++) { // Try sending a message to each try { // If the message is blank or the connection is null, break out if (Message.Trim() == "" || tcpClients[i] == null) { continue; } // Send the message to the current user in the loop swSenderSender = new StreamWriter(tcpClients[i].GetStream()); swSenderSender.WriteLine("Administrator: " + Message); swSenderSender.Flush(); swSenderSender = null; } catch // If there was a problem, the user is not there anymore, remove him { RemoveUser(tcpClients[i]); } } } // Send messages from one user to all the others public static void SendMessage(string From, string Message) { StreamWriter swSenderSender; // First of all, show in our application who says what e = new StatusChangedEventArgs(From + " says: " + Message); OnStatusChanged(e); // Create an array of TCP clients, the size of the number of users we have TcpClient[] tcpClients = new TcpClient[ChatServer.htUsers.Count]; // Copy the TcpClient objects into the array ChatServer.htUsers.Values.CopyTo(tcpClients, 0); // Loop through the list of TCP clients for (int i = 0; i < tcpClients.Length; i++) { // Try sending a message to each try { // If the message is blank or the connection is null, break out if (Message.Trim() == "" || tcpClients[i] == null) { continue; } // Send the message to the current user in the loop swSenderSender = new StreamWriter(tcpClients[i].GetStream()); swSenderSender.WriteLine(From + " says: " + Message); swSenderSender.Flush(); swSenderSender = null; } catch // If there was a problem, the user is not there anymore, remove him { RemoveUser(tcpClients[i]); } } } public void StartListening() { // Get the IP of the first network device, however this can prove unreliable on certain configurations IPAddress ipaLocal = ipAddress; // Create the TCP listener object using the IP of the server and the specified port tlsClient = new TcpListener(1986); // Start the TCP listener and listen for connections tlsClient.Start(); // The while loop will check for true in this before checking for connections ServRunning = true; // Start the new tread that hosts the listener thrListener = new Thread(KeepListening); thrListener.Start(); } private void KeepListening() { // While the server is running while (ServRunning == true) { // Accept a pending connection tcpClient = tlsClient.AcceptTcpClient(); // Create a new instance of Connection Connection newConnection = new Connection(tcpClient); } } } // This class handels connections; there will be as many instances of it as there will be connected users class Connection { TcpClient tcpClient; // The thread that will send information to the client private Thread thrSender; private StreamReader srReceiver; private StreamWriter swSender; private string currUser; private string strResponse; // The constructor of the class takes in a TCP connection public Connection(TcpClient tcpCon) { tcpClient = tcpCon; // The thread that accepts the client and awaits messages thrSender = new Thread(AcceptClient); // The thread calls the AcceptClient() method thrSender.Start(); } private void CloseConnection() { // Close the currently open objects tcpClient.Close(); srReceiver.Close(); swSender.Close(); } // Occures when a new client is accepted private void AcceptClient() { srReceiver = new System.IO.StreamReader(tcpClient.GetStream()); swSender = new System.IO.StreamWriter(tcpClient.GetStream()); // Read the account information from the client currUser = srReceiver.ReadLine(); // We got a response from the client if (currUser != "") { // Store the user name in the hash table if (ChatServer.htUsers.Contains(currUser) == true) { // 0 means not connected swSender.WriteLine("0|This username already exists."); swSender.Flush(); CloseConnection(); return; } else if (currUser == "Administrator") { // 0 means not connected swSender.WriteLine("0|This username is reserved."); swSender.Flush(); CloseConnection(); return; } else { // 1 means connected successfully swSender.WriteLine("1"); swSender.Flush(); // Add the user to the hash tables and start listening for messages from him ChatServer.AddUser(tcpClient, currUser); } } else { CloseConnection(); return; } try { // Keep waiting for a message from the user while ((strResponse = srReceiver.ReadLine()) != "") { // If it's invalid, remove the user if (strResponse == null) { ChatServer.RemoveUser(tcpClient); } else { // Otherwise send the message to all the other users ChatServer.SendMessage(currUser, strResponse); } } } catch { // If anything went wrong with this user, disconnect him ChatServer.RemoveUser(tcpClient); } } } }
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Threading; using System.Net; using System.Net.Sockets; using System.IO; namespace ChatServer { public partial class Form1 : Form { private delegate void UpdateStatusCallback(string strMessage); public Form1() { InitializeComponent(); } private void btnListen_Click(object sender, EventArgs e) { // Разбор IP-адрес сервера из TextBox IPAddress ipAddr = IPAddress.Parse(txtIp.Text); // Создайте новый экземпляр объекта ChatServer ChatServer mainServer = new ChatServer(ipAddr); // Наденьте StatusChanged обработчик события mainServer_StatusChanged ChatServer.StatusChanged += new StatusChangedEventHandler(mainServer_StatusChanged); // Начать прослушивание соединений mainServer.StartListening(); // Показать, что мы начали ожидать соединения txtLog.AppendText("Monitoring for connections...\r\n"); } public void mainServer_StatusChanged(object sender, StatusChangedEventArgs e) { // Вызовите метод, который обновляет форму this.Invoke(new UpdateStatusCallback(this.UpdateStatus), new object[] { e.EventMessage }); } private void UpdateStatus(string strMessage) { // Обновления журнал с сообщением txtLog.AppendText(strMessage + "\r\n"); } } }
Решение задачи: «Как исправить ошибку при отключении клиента от сервера»
textual
Листинг программы
while (Connected != 0) { if (Connected != 0) { try { this.Invoke(new UpdateLogCallback(this.UpdateLog), new object[] { srReceiver.ReadLine() }); } catch (Exception ex) { MessageBox.Show(ex.Message); } } else { break; } // Показать сообщения в журнале TextBox }
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д