How to turn off the client from the TCP server



  • There's a client-serial app.

    Client:

    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;
    

    namespace Client
    {

    public partial class Form1 : Form
    {
    Thread receiveThread;
    static string userName = null;
    static TcpClient client;
    static NetworkStream stream;
    private const string host = "127.0.0.1";
    private const int port = 8888;

    public Form1()
    {
        InitializeComponent();
        chatTextBox.ForeColor = Color.Black;
    
    }
    
    private void loginButton_Click(object sender, EventArgs e)
    {
        if (userNameTextBox.TextLength <= 2)
        {
            MessageBox.Show("Минимальная длина имени 3 символа.");
        }
        else
        {
            userName = userNameTextBox.Text;
            client = new TcpClient();
            try
            {
                client.Connect(host, port); // подключение клиента
                stream = client.GetStream(); // получаем поток отправки и получения данных
    
                if (client.Connected) // если подключение к серверу успешно
                {
                    messageTextBox.Enabled = true; // разрешаем вводить сообщения для отправки
                    sendButton.Enabled = true; // разрешаем отправлять сообщения серверу
                    logoutButton.Enabled = true; // делаем активной кнопку выхода из чата
    
                    userNameTextBox.Enabled = false;
                    loginButton.Enabled = false;
    
                    // Отправляем имя
                    byte[] data = Encoding.Unicode.GetBytes(userName);
                    stream.Write(data, 0, data.Length);
    
                    // Запускаем новый поток для получения данных
                    receiveThread = new Thread(new ThreadStart(ReceiveMessage));
                    receiveThread.Start(); // запускаем поток
                    chatTextBox.Text += "Добро пожаловать, " + userName + "\r\n";
                }
                else
                {
                    chatTextBox.Text += "Отсутствует подключение к серверу!\r\n";
                }
            }
            catch (Exception exc)
            {
                Console.WriteLine(exc.Message);
                Disconnect();
            }
        }
    }
    
    // Отправка данных
    private void sendButton_Click(object sender, EventArgs e)
    {
        string message = messageTextBox.Text;
        byte[] data = Encoding.Unicode.GetBytes(message);
        stream.Write(data, 0, data.Length);
    
        chatTextBox.Text += string.Format($"Вы: {message}\r\n");
        messageTextBox.Text = "";
    }
    
    // Получение данных
    private void ReceiveMessage()
    {
        while (true)
        {
            try
            {
                byte[] data = new byte[128]; // буфер для получаемых данных
                StringBuilder getString = new StringBuilder();
                int bytes = 0;
                do
                {
                    bytes = stream.Read(data, 0, data.Length);
                    getString.Append(Encoding.Unicode.GetString(data, 0, bytes));
                }
                while (stream.DataAvailable);
    
    
                //chatTextBox.Invoke(
                //    (ThreadStart)delegate ()
                //    {
                //        chatTextBox.Text += string.Format($"{getString.ToString()}\r\n");
                //    });
                chatTextBox.Text += string.Format($"{getString.ToString()}\r\n");
            }
            catch
            {
                chatTextBox.Invoke(
                    (ThreadStart)delegate ()
                    {
                        chatTextBox.Text += string.Format("Подключение прервано!");
                    });
                //chatTextBox.Text += string.Format("Подключение прервано!");
                Disconnect();
                break;
            }
        }
    }
    
    private void Disconnect()
    {
        if (stream != null)
            stream.Close();
        if (client != null)
            client.Close();
    }
    
    private void logoutButton_Click(object sender, EventArgs e)
    {
        receiveThread.Abort();
        receiveThread.Join(500);
    
        Disconnect();
    
        userNameTextBox.Enabled = true;
        loginButton.Enabled = true;
    
        messageTextBox.Enabled = false;
        sendButton.Enabled = false;
        logoutButton.Enabled = false;
    
    }
    

    }
    }

    Server:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Net;
    using System.Net.Sockets;

    namespace Server
    {
    public class ClientObject
    {
    internal string Id { get; private set; }
    internal NetworkStream Stream { get; set; }
    string userName;
    TcpClient client;
    ServerObject server; // объект сервера

    public ClientObject(TcpClient tcpClient, ServerObject serverObject)
    {
        Id = Guid.NewGuid().ToString();
        client = tcpClient;
        server = serverObject;
        server.AddConnection(this);
    }
    
    public void Process()
    {
        try
        {
            Stream = client.GetStream();
            // Получаем имя пользователя
            userName = GetMessage();
            string message = userName + " вошел в чат.";
            // Рассылаем сообщение о входе в чат всем подключенным пользователям
            server.BroadcastMessage(message, Id);
            Console.WriteLine(message);
    
            // В бесконечном цикле получаем данные от пользователя
            while (true)
            {
                try
                {
                    message = GetMessage();
                    message = string.Format($"{userName}: {message}");
                    server.BroadcastMessage(message, Id);
                    Console.WriteLine(message);
                }
                catch
                {
                    message = userName + " покинул чат.";
                    server.BroadcastMessage(message, Id);
                    Console.WriteLine(message);
                    break;
                }
            }
        }
        catch (Exception exc)
        {
            Console.WriteLine(exc.Message);
        }
        finally
        {
            // Удаляем пользователя из списка подключенных пользователей и закрываем поток с соединением
            server.RemoveConnection(Id);
            Close();
        }
    }
    
    internal string GetMessage()
    {
        byte[] data = new byte[128];
        StringBuilder getString = new StringBuilder();
        int bytes = 0;
        do
        {
            bytes = Stream.Read(data, 0, data.Length);
            getString.Append(Encoding.Unicode.GetString(data, 0, bytes));
        }
        while (Stream.DataAvailable);
    
        return getString.ToString();
    }
    
    internal void Close()
    {
        if (Stream != null)
            Stream.Close(); // закрываем поток
        if (client != null)
            client.Close(); // закрываем соединение
    }
    

    }

    public class ServerObject
    {
    TcpListener listener;
    List<ClientObject> clients = new List<ClientObject>();

    // Добавляем пользователя в список подключенных пользователей
    internal void AddConnection(ClientObject clientObject)
    {
        clients.Add(clientObject);
    }
    
    // Отправляем сообщение всем пользователям, кроме отправителя
    internal void BroadcastMessage(string message, string id)
    {
        byte[] data = Encoding.Unicode.GetBytes(message);
        for (int i = 0; i &lt; clients.Count; i++)
        {
            if (clients[i].Id != id)
            {
                clients[i].Stream.Write(data, 0, data.Length);
            }
        }
    }
    
    // Удаляем пользователя из списка подключенных пользователей
    internal void RemoveConnection(string id)
    {
        ClientObject client = clients.FirstOrDefault(i =&gt; i.Id == id);
        if (client != null)
            clients.Remove(client);
    }
    
    public void Listen()
    {
        try
        {
            listener = new TcpListener(IPAddress.Any, 8888);
            listener.Start(); // запуск прослушивания входящих запросов
            Console.WriteLine("Ожидание подключений...");
    
            while (true)
            {
                TcpClient tcpClient = listener.AcceptTcpClient();
    
                ClientObject clientObject = new ClientObject(tcpClient, this);
                Thread clientThread = new Thread(new ThreadStart(clientObject.Process));
                clientThread.Start(); // старт потока
            }
        }
        catch(Exception exc)
        {
            Console.WriteLine(exc.Message);
            Disconnect();
        }
    }
    
    internal void Disconnect()
    {
        listener.Stop(); // отсанавливаем сервер
    
        for (int i = 0; i &lt; clients.Count; i++)
        {
            clients[i].Close();
        }
    }
    

    }

    class Program
    {
    static ServerObject server;
    static Thread listenerThread;

    static void Main(string[] args)
    {
        try
        {
            server = new ServerObject();
            listenerThread = new Thread(new ThreadStart(server.Listen));
            listenerThread.Start(); // старт потока
        }
        catch (Exception exc)
        {
            Console.WriteLine(exc.Message);
            server.Disconnect();
        }
    }
    

    }
    }

    The problem is, when pressed on the button, the server's out is starting to get empty messages from the client, like every second the client sends a message 10 times without a text, although the client's off. How can you fix that?



  • This is the code:

    do
    {
        bytes = Stream.Read(data, 0, data.Length);
        getString.Append(Encoding.Unicode.GetString(data, 0, bytes));
    }
    while (Stream.DataAvailable);
    

    Absolutely not.

    You can't assume one message from a client will come by one piece. Communications can and will be fragmented because TCP is a flux format. Reading before exhausting the data in the flow is therefore incorrect, the tail of the data may come later.

    In addition, this method implies that data will necessarily come. If the data didn't come (e.g. the client doesn't send anything else), it's still gonna get something back.

    In addition, this method tries to interpret partial data as a line in UTF-16. If there's an inaccurate number of Byte (or the surrogate will be cut off), this will lead to an exception.


    Usually the following trick is used: its length is attached to the message, and the code reads the baytes until the required number of byte. When all the bikes are counted, we can start decoding the message.




Suggested Topics

  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2