C # проблема с синхронизацией передачи токенов в клиент-серверном чате

#c# #synchronization #network-programming #client-server #token

#c# #синхронизация #сетевое программирование #клиент-сервер #токен

Вопрос:

У меня проблема с передачей токенов в клиент-серверном приложении C #. Не могли бы вы помочь мне с этим, пожалуйста? Во-первых, я описываю ситуацию, во-вторых, вставляю некоторый исходный код, в-третьих, я дам ссылку на решения Visual studio.

Итак, ситуация такая: я создал многопоточный TCP-чат клиент-сервер. Все работает нормально, все пользователи могут писать сообщения одновременно, и все пользователи все видят.

Проблема в том, что мне нужно, чтобы сервер передавал токен одному пользователю за раз, поэтому только один пользователь может отправлять сообщения одновременно, когда все остальные пользователи будут только прослушивать. Токен должен передаваться пользователям в порядке присоединения к серверу в течение 10 секунд.

Пример: пользователь A присоединился к серверу, пользователь B присоединился к серверу, пользователь C присоединился к серверу. Сервер передает токен пользователю A в течение 10 секунд, и пользователь A может писать сообщения в течение 10 секунд (например, кнопка отправки по умолчанию отключена, а сервер передает токен клиенту = true). Если токен имеет значение true, кнопка отправки включена на 10 секунд, а затем токен снова имеет значение false). Через 10 секунд сервер передает токен пользователю B, и пользователь B может писать сообщения в течение 10 секунд и т.д.. Как это сделать?

Вот исходный код сервера:

 namespace Serverchat
{
class Serwer
{
    public static Hashtable klienci = new Hashtable();

    static void Main(string[] args)
    {
        IPAddress IP = IPAddress.Parse("127.0.0.1");
        int port = 8888;

        TcpListener serwer = new TcpListener(IP, port);
        TcpClient gniazdo = default(TcpClient);

        serwer.Start();
        Console.WriteLine("Token passing simulationrnAddress: " IP ":" port "rn");

        while (true)
        {
            gniazdo = serwer.AcceptTcpClient();
            byte[] odczyt = new byte[10024];
            string odczytsub = "";

            gniazdo.GetStream().Read(odczyt, 0, gniazdo.ReceiveBufferSize);
            odczytsub = (Encoding.ASCII.GetString(odczyt)).Substring(0, (Encoding.ASCII.GetString(odczyt)).IndexOf("~"));
            klienci.Add(odczytsub, gniazdo);
            rozglos("", odczytsub);
            Console.WriteLine(odczytsub   " joined server.");
            obslugaKlienta klient = new obslugaKlienta();
            klient.startObslugiKlienta(gniazdo, odczytsub);
        }
    }

    public static void rozglos(string wiadomosc, string nazwaUzytkownika)
    {
        foreach (DictionaryEntry klient in klienci)
        {
            TcpClient gniazdo = (TcpClient)klient.Value;
            Byte[] zapis = null;

            if (wiadomosc != "")
            {
                zapis = Encoding.ASCII.GetBytes(nazwaUzytkownika   ":"   wiadomosc   "`");
            }
            else
            {
                zapis = Encoding.ASCII.GetBytes(nazwaUzytkownika   " joined server:");
            }
            gniazdo.GetStream().Write(zapis, 0, zapis.Length);
            gniazdo.GetStream().Flush();
        }
    }
}

public class obslugaKlienta
{
    TcpClient gniazdo;
    string klient;

    public void startObslugiKlienta(TcpClient gniazdo, string klient)
    {
        this.gniazdo = gniazdo;
        this.klient = klient;
        Thread klientWatek = new Thread(komunikacja);
        klientWatek.Start();
    }

    private void komunikacja()
    {
        byte[] odczyt = new byte[10024];
        string odczytsub = "";

        while (true)
        {
            gniazdo.GetStream().Read(odczyt, 0, gniazdo.ReceiveBufferSize);
            odczytsub = Encoding.ASCII.GetString(odczyt).Substring(0, Encoding.ASCII.GetString(odczyt).IndexOf("~"));
            Console.WriteLine(klient   ": "   odczytsub);
            Serwer.rozglos(Convert.ToString(odczytsub), klient);
        }
    }
}
}
  

Для справки, rozglos — это функция, которая транслирует сообщения всем.

Вот исходный код клиента:

 namespace TRKlient
{
public partial class Klient : Form
{
    TcpClient gniazdo = new TcpClient();
    byte[] zapis;
    string dane = null;

    private void buttonWyslij_Click(object sender, EventArgs e) // Sending message
    {
        zapis = Encoding.ASCII.GetBytes(tbWiadomosc.Text   "~");
        gniazdo.GetStream().Write(zapis, 0, zapis.Length);
        gniazdo.GetStream().Flush();
    }

    private void buttonPolacz_Click(object sender, EventArgs e) // Connecting with server
    {
        dane = "Connected with Token Ring.";
        wyswietlWiadomosc();
        gniazdo.Connect(tbIP.Text, 8888);
        zapis = Encoding.ASCII.GetBytes(tbUser.Text   "~");
        gniazdo.GetStream().Write(zapis, 0, zapis.Length);
        gniazdo.GetStream().Flush();
        Thread klientWatek = new Thread(odbierzWiadomosc);
        klientWatek.Start();
        buttonPolacz.Enabled = false;
    }

    private void odbierzWiadomosc() // Reading data from stream
    {
        while (true)
        {
            byte[] odczyt = new byte[10024];
            gniazdo.GetStream().Read(odczyt, 0, gniazdo.ReceiveBufferSize);
            dane = Encoding.ASCII.GetString(odczyt);
            wyswietlWiadomosc();
        }
    }

    private void wyswietlWiadomosc() // Shows received messages in chat textbox
    {
        if (this.InvokeRequired)
            this.Invoke(new MethodInvoker(wyswietlWiadomosc));
        else
        tbChat.Text  = "rn # "   dane;
    }

    public Klient() 
    {
        InitializeComponent();
    }
}
}
  

Вот ссылка на оба решения в Visual Studio 2010: http://www.speedyshare.com/files/28562696/client-server.rar

Пожалуйста, помогите мне, ребята, это очень важно для меня, и у меня закончились идеи.

Ты помогал мне много раз, так что заранее спасибо, Питер.

РЕДАКТИРОВАТЬ: Отправка сообщения клиентом с токеном может быть такой же простой, как автоматическая отправка первой буквы его псевдонима. Все может быть максимально простым, мне нужна только передача рабочего токена. Спасибо за ваши ответы.

Ответ №1:

давайте посмотрим на различия между старыми и новыми требованиями:

ранее вы хотели отправлять сообщения с клиента на сервер, а оттуда всем (другим) клиентам, что отлично работает, если вы передаете сообщение в виде строки, без какого-либо другого протокола… если клиент что-то получает, вы можете быть уверены, что это строка сообщения, которая должна отображаться в текстовом поле вашего чата…

теперь все немного по-другому: вы должны различать сообщения, которые должны отображаться, и управляющие сообщения, которые информируют о токене … для этого вам нужен какой-то протокол…

что-то, что сообщает слушателю, содержит ли сообщение сообщение чата или управляющее сообщение.

управляющие сообщения информируют клиента о таких вещах, как «привет, клиент… вы получили токен… отправка разрешена в течение 10 секунд …», или «ваше разрешение на отправку отозвано» и т.д.

на основе этих сообщений вы можете включить / отключить кнопку отправки вашего клиента…

с другой стороны, вам также придется реализовать серверную часть: управлять токеном и отправлять соответствующие управляющие сообщения клиентам.

Комментарии:

1. Спасибо за ваш ответ, я понял вашу точку зрения. К сожалению, я до сих пор не знаю, как это сделать на практике: (

2. сначала вам нужно будет определить протокол для этих сообщений. вы можете определить свой собственный или реализовать что-то вроде протокола IRC ( faqs.org/rfcs/rfc1459.html ) но IRC кажется немного перегруженным для вашего приложения. возможно, просто добавляйте к каждому сообщению чата сообщение MSG, а управляющими сообщениями для токена могут быть «ТОКЕН <время в секундах>». вы можете отличить сообщение чата от токена, просто взглянув на первое слово, а затем передать строку соответствующей функции обработки (chat-message? -> вывод пользователю / управляющему сообщению? -> сделай что-нибудь еще)