异步聊天服务器缓冲区问题 [英] Async chat server buffer issue

查看:135
本文介绍了异步聊天服务器缓冲区问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有人可以请帮我这个......我整天都在挣扎。

can someone please help me out with this... I've been struggling all day.

所以我想了解异步套接字这恐怕是一直给我的麻烦。

So I'm trying to learn Async sockets which is something that's been giving me trouble.

问题基本上是我更新与人列表框的方式谁也加入了聊天室的名称:

The issue is basically the way I'm updating the ListBox with people who have joined the chat room's names:

基本上我做的是让每个客户端发送的!! addlist [昵称]当他们加入的服务器。

Basically what I'm doing is having each client send "!!addlist [nickname]" when they join the server.

这不是理想的,因为它没有重复检查等,但现在我只是想知道为什么它不会工作。
每当有人补充说,他们以前从未见过一个名字,他们也将派!! addlist [尼克]

It's not ideal as it doesn't check for duplicates etc. but now I just want to know why it won't work. Whenever somebody adds a name they haven't seen before, they will also send "!!addlist [nick]"

在这种方式,每次有人加入,名单应该为大家更新。
这个问题似乎是,所有的客户端启动的同时进行通信,并与缓冲干扰

In this way, every time someone joins, the lists should be updated for everyone. The issue seems to be that all the clients start communicating at the same time and it interferes with the buffer.

我使用一个单独的缓存为每个客户端所以这不是问题的尝试。
我已经尝试过使用的锁(),但似乎并没有被任何工作。

I've tried using a separate buffer for every client so that's not the issue. I've tried using lock() but that doesn't seem to be working either.

从本质上发生的事情是缓冲区似乎截断;那里是在相同的缓冲液从两个不同的人的数据。

Essentially what happens is the buffers seem to truncate; where there is data from two different people in the same buffer.

请告诉我我在做什么错误的缓冲区或在客户端:

Please just tell me what I'm doing wrong with the buffers or on the client side:

请注意,异步套接字使用发送,而不是BeginSend。
我已经试过这两种方法,他们碰上了同样的问题...所以它可能是客户端?

Note that the async socket is using Send instead of BeginSend. I've tried both methods and they run into the same issue... so it's probably client side?

public partial class Login : Form
{
    private ChatWindow cw;
    private Socket serverSocket;
    private List<Socket> socketList;
    private byte[] buffer;
    private bool isHost;
    private bool isClosing;

    public void startListening()
    {
        try
        {
            this.isHost = true;                                                         //We're hosting this server
            cw.callingForm = this;                                                      //Give ChatForm the login form (this) [that acts as the server]
            cw.Show();                                                                  //Show ChatForm
            cw.isHost = true;                                                           //Tell ChatForm it is the host (for display purposes)
            this.Hide();                                                                //And hide the login form
            serverSocket.Bind(new IPEndPoint(IPAddress.Any, int.Parse(portBox.Text)));  //Bind to our local address
            serverSocket.Listen(1);                                                     //And start listening
            serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);          //When someone connects, begin the async callback
            cw.connectTo("127.0.0.1", int.Parse(portBox.Text), nicknameBox.Text);       //And have ChatForm connect to the server
        }
        catch (Exception) { /*MessageBox.Show("Error:\n\n" + e.ToString());*/ }           //Let us know if we ran into any errors
    }

    public void AcceptCallback(IAsyncResult AR)
    {
        try
        {
            Socket s = serverSocket.EndAccept(AR);                                                              //When someone connects, accept the new socket
            socketList.Add(s);                                                                                  //Add it to our list of clients
            s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s);  //Begin the async receive method using our buffer
            serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);                                  //And start accepting new connections
        }
        catch (Exception) {}
    }

    public void ReceiveCallback(IAsyncResult AR)                //When a message from a client is received
    {
        try
        {
            if (isClosing)
                return;

            Socket s = (Socket)AR.AsyncState;                   //Get the socket from our IAsyncResult

            int received = s.EndReceive(AR);                    //Read the number of bytes received (*need to add locking code here*)
            byte[] dbuf = new byte[received];                   //Create a temporary buffer to store just what was received so we don't have extra data

            Array.Copy(buffer, dbuf, received);                 //Copy the received data from our buffer to our temporary buffer

            foreach (Socket client in socketList)               //For each client that is connected
            {
                try
                {
                    if (client != (Socket)AR.AsyncState)        //If this isn't the same client that just sent a message (*client handles displaying these*)
                        client.Send(dbuf);                      //Send the message to the client
                }
                catch (Exception) {  }
            }                                                  //Start receiving new data again
            s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s);
        }
        catch (Exception) { /*cw.output("\n\nError:\n\n" + e.ToString());*/ }
    }

    public void SendCallback(IAsyncResult AR)
    {
        try
        {
            Socket s = (Socket)AR.AsyncState;
            s.EndSend(AR);
        }
        catch (Exception) { /*cw.output("\n\nError:\n\n" + e.ToString());*/ }
    }

下面是客户端:

    public void getData()
    {
        try
        {
            byte[] buf = new byte[1024];
            string message = "";
            while(isConnected)
            {
                Array.Clear(buf, 0, buf.Length);
                message = "";
                clientSocket.Receive(buf, buf.Length, SocketFlags.None);
                message = Encoding.ASCII.GetString(buf);
                if (message.StartsWith("!!addlist"))
                {
                    message = message.Replace("!!addlist", "");
                    string userNick = message.Trim();
                    if (!namesBox.Items.Contains(userNick))
                    {
                        addNick(userNick.Trim());
                    }
                    continue;
                }
                else if (message.StartsWith("!!removelist"))
                {
                    message = message.Replace("!!removelist", "");
                    string userNick = message.Trim();
                    removeNick(userNick);
                    output("Someone left the room: " + userNick);
                    continue;
                }
                else if (!namesBox.Items.Contains(message.Substring(0, message.IndexOf(":"))))
                {
                    addNick(message.Substring(0, message.IndexOf(":")).Trim()); //So they at least get added when they send a message
                }
                output(message);
            }
        }
        catch (Exception)
        {
            output("\n\nConnection to the server lost.");
            isConnected = false;
        }
    }

这是我迟钝addNick功能,这似乎解决一些事情呢?

    public void addNick(string n)
    {
        if (n.Contains(" ")) //No Spaces... such a headache
            return;
        if (n.Contains(":"))
            return;
        bool shouldAdd = true;
        n = n.Trim();
        for (int x = namesBox.Items.Count - 1; x >= 0; --x)
            if (namesBox.Items[x].ToString().Contains(n))
                shouldAdd = false;
        if (shouldAdd)
        {
            namesBox.Items.Add(n);
            output("Someone new joined the room: " + n);
            sendRaw("!!addlist " + nickName);
        }
    }

我认为这个问题是,一些数据包被跳过?

也许有太多$在客户C $ C之后,得到之前被再次呼吁?

我应该为每一个消息,以便不断收到运行一个单独的线程? (阿呆)

我应该有我的客户端使用异步接收和发送,以及?

我有一种感觉,那就是答案^

与所有我做检查,我设法清理重复名称的问题...但我经常收到来自其他客户的空间和部分消息的消息似乎。

With all of the checks I do, I managed to clean up the duplicate name issue... but i regularly receive messages with spaces and partial messages from other clients it seems.

推荐答案

好了,所以,这个搞乱了很久之后,我有比较稳定的。

Okay so, after messing with this for a long time, I have it relatively stable.

对于初学者来说,我增加了以下状态对象:

For starters, I added the following state object:

public class StateObject
{
    public Socket workSocket = null;
    public const int BufferSize = 1024;
    public byte[] buffer = new byte[BufferSize];
    public StringBuilder sb = new StringBuilder();
    public bool newConnection = true;
}

这可以很容易地保持每个连接的轨道,给每个连接它自己的缓冲区。

This makes it easy to keep track of each connection and gives each connection its own buffer.

我做的第二件事是看每封邮件中的新行。
我不是在原来的code找这个,我相信这是大多数问题的根源。

The second thing I did was look for a new line in each message. I wasn't looking for this in the original code and I believe this was the root of most issues.

我也给处理的用户名管理服务器的责任;东西,我应该从一开始就显然已经做了。

I also gave the responsibility of dealing with username management to the server; something that I should have done from the start obviously.

这是当前服务器code:

这code是绝不完美,我不断地寻找新的错误越多我试图打破它。我要保持与它搞乱一段时间,但在目前,它似乎体面的工作。

This code is in no way perfect and I'm continuously finding new errors the more I try to break it. I'm going to keep messing with it for awhile but at the moment, it seems to work decently.

public partial class Login : Form
{
    private ChatWindow cw;
    private Socket serverSocket;
    private List<Socket> socketList;
    private byte[] buffer;
    private bool isHost;
    private bool isClosing;
    private ListBox usernames;

    public Login()
    {
        InitializeComponent();
    }

    private void Login_Load(object sender, EventArgs e)
    {
        ipLabel.Text = getLocalIP();
        cw = new ChatWindow();
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socketList = new List<Socket>();
        buffer = new byte[1024];
        isClosing = false;
        usernames = new ListBox();
    }

    public string getLocalIP()
    {
        return Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToString();
    }

    private void joinButton_Click(object sender, EventArgs e)
    {
        try
        {
            int tryPort = 0;
            this.isHost = false;
            cw.callingForm = this;
            if (ipBox.Text == "" || portBox.Text == "" || nicknameBox.Text == "" || !int.TryParse(portBox.Text.ToString(), out tryPort))
            {
                MessageBox.Show("You must enter an IP Address, Port, and Nickname to connect to a server.", "Missing Info");
                return;
            }
            this.Hide();
            cw.Show();
            cw.connectTo(ipBox.Text, int.Parse(portBox.Text), nicknameBox.Text);
        }
        catch(Exception otheree) {
            MessageBox.Show("Error:\n\n" + otheree.ToString(),"Error connecting...");
            cw.Hide();
            this.Show();
        }
    }

    private void hostButton_Click(object sender, EventArgs e)
    {
        int tryPort = 0;
        if (portBox.Text == "" || nicknameBox.Text == "" || !int.TryParse(portBox.Text.ToString(), out tryPort)) {
            MessageBox.Show("You must enter a Port and Nickname to host a server.", "Missing Info");
            return;
        }
        startListening();
    }

    public void startListening()
    {
        try
        {
            this.isHost = true;                                                         //We're hosting this server
            cw.callingForm = this;                                                      //Give ChatForm the login form (this) [that acts as the server]
            cw.Show();                                                                  //Show ChatForm
            cw.isHost = true;                                                           //Tell ChatForm it is the host (for display purposes)
            this.Hide();                                                                //And hide the login form
            serverSocket.Bind(new IPEndPoint(IPAddress.Any, int.Parse(portBox.Text)));  //Bind to our local address
            serverSocket.Listen(1);                                                     //And start listening
            serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);          //When someone connects, begin the async callback
            cw.connectTo("127.0.0.1", int.Parse(portBox.Text), nicknameBox.Text);       //And have ChatForm connect to the server
        }
        catch (Exception) {}
    }

    public void AcceptCallback(IAsyncResult AR)
    {
        try
        {
            StateObject state = new StateObject();
            state.workSocket = serverSocket.EndAccept(AR);
            socketList.Add(state.workSocket);
            state.workSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), state);
            serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
        }
        catch (Exception) {}
    }

    public void ReceiveCallback(IAsyncResult AR)
    {
        try
        {
            if (isClosing)
                return;

            StateObject state = (StateObject)AR.AsyncState;
            Socket s = state.workSocket;
            String content = "";
            int received = s.EndReceive(AR);

            if(received > 0)
                state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, received));

            content = state.sb.ToString();

            if (content.IndexOf(Environment.NewLine) > -1) //If we've received the end of the message
            {

                if (content.StartsWith("!!addlist") && state.newConnection)
                {
                    state.newConnection = false;
                    content = content.Replace("!!addlist", "");
                    string userNick = content.Trim();
                    if (isHost && userNick.StartsWith("!"))
                        userNick = userNick.Replace("!", "");
                    userNick = userNick.Trim();
                    if (userNick.StartsWith("!") || userNick == string.Empty || usernames.Items.IndexOf(userNick) > -1)
                    {
                        //Invalid Username :c get dropped
                        s.Send(Encoding.ASCII.GetBytes("Invalid Username/In Use - Sorry :("));
                        s.Shutdown(SocketShutdown.Both);
                        s.Disconnect(false);
                        s.Close();
                        socketList.Remove(s);
                        return;
                    }
                    usernames.Items.Add(userNick);
                    foreach (string name in usernames.Items)
                    {
                        if (name.IndexOf(userNick) < 0)
                        {
                            s.Send(Encoding.ASCII.GetBytes("!!addlist " + name + "\r\n"));
                            Thread.Sleep(10); //such a hack... ugh it annoys me that this works
                        }
                    }
                    foreach (Socket client in socketList)
                    {
                        try
                        {
                            if (client != s)
                                client.Send(Encoding.ASCII.GetBytes("!!addlist " + userNick + "\r\n"));
                        }
                        catch (Exception) { }
                    }
                }
                else if (content.StartsWith("!!removelist") && !state.newConnection)
                {
                    content = content.Replace("!!removelist", "");
                    string userNick = content.Trim();
                    usernames.Items.Remove(userNick);
                    foreach (Socket client in socketList)
                    {
                        try
                        {
                            if (client != s)
                                client.Send(Encoding.ASCII.GetBytes("!!removelist " + userNick + "\r\n"));
                        }
                        catch (Exception) { }
                    }
                }
                else if (state.newConnection) //if they don't give their name and try to send data, just drop.
                {
                    s.Shutdown(SocketShutdown.Both);
                    s.Disconnect(false);
                    s.Close();
                    socketList.Remove(s);
                    return;
                }
                else
                {
                    foreach (Socket client in socketList)
                    {
                        try
                        {
                            if (client != s)
                                client.Send(System.Text.Encoding.ASCII.GetBytes(content));
                        }
                        catch (Exception) { }
                    }
                }
            }
            Array.Clear(state.buffer, 0, StateObject.BufferSize);
            state.sb.Clear();
            s.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
        }
        catch (Exception) {
            socketList.Remove(((StateObject)AR.AsyncState).workSocket);
        }
    }
    public void SendCallback(IAsyncResult AR)
    {
        try
        {
            StateObject state = (StateObject)AR.AsyncState;
            state.workSocket.EndSend(AR);
        }
        catch (Exception) {}
    }
    private void Login_FormClosed(object sender, FormClosedEventArgs e)
    {
        try
        {
            this.isClosing = true;
            if (this.isHost)
            {
                foreach (Socket c in socketList)
                {
                    if (c.Connected)
                    {
                        c.Close();
                    }
                }
                serverSocket.Shutdown(SocketShutdown.Both);
                serverSocket.Close();
                serverSocket = null;
                serverSocket.Dispose();
            }
            socketList.Clear();
        }
        catch (Exception) { }
        finally
        {
            Application.Exit();
        }
    }
}
public class StateObject
{
    public Socket workSocket = null;
    public const int BufferSize = 1024;
    public byte[] buffer = new byte[BufferSize];
    public StringBuilder sb = new StringBuilder();
    public bool newConnection = true;
}

客户端code(工作正在进行中):

public partial class ChatWindow : Form
{
    private Socket clientSocket;
    private Thread chatThread;
    private string ipAddress;
    private int port;
    private bool isConnected;
    private string nickName;
    public bool isHost;
    public Login callingForm;

    private static object conLock = new object();

    public ChatWindow()
    {
        InitializeComponent();
        isConnected = false;
        isHost = false;
    }

    public string getIP() {
        return Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToString();
    }

    public void displayError(string err)
    {
        output(Environment.NewLine + Environment.NewLine + err + Environment.NewLine);
    }

    public void op(string s)
    {
        try
        {
            lock (conLock)
            {
                chatBox.Text += s;
            }
        }
        catch (Exception) { }
    }

    public void connectTo(string ip, int p, string n) {
        try
        {
            this.Text = "Trying to connect to " + ip + ":" + p + "...";
            this.ipAddress = ip;
            this.port = p;
            this.nickName = n;

            clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            if (!isHost)
            {
                op("Connecting to " + ipAddress + ":" + port + "...");
            }
            else
            {
                output("Listening on " + getIP() + ":" + port + "...");
            }

            clientSocket.Connect(ipAddress, port);

            isConnected = true;

            if (!isHost)
            {
                this.Text = "Connected to " + ipAddress + ":" + port + " - Nickname: " + nickName;
                output("Connected!");
            }
            else
            {
                this.Text = "Hosting on " + getIP() + ":" + port + " - Nickname: " + nickName;
            }

            chatThread = new Thread(new ThreadStart(getData));
            chatThread.Start();

            nickName = nickName.Replace(" ", "");
            nickName = nickName.Replace(":", "");
            if(nickName.StartsWith("!"))
                nickName = nickName.Replace("!", "");
            namesBox.Items.Add(nickName);

            sendRaw("!!addlist " + nickName);
        }
        catch (ThreadAbortException)
        {
            //do nothing; probably closing chat window
        }
        catch (Exception e)
        {
            if (!isConnected)
            {
                this.Hide();
                callingForm.Show();
                clearText();
                MessageBox.Show("Error:\n\n" + e.ToString(), "Error connecting to remote host");
            }
        }
    }

    public void removeNick(string n)
    {
        if (namesBox.Items.Count <= 0)
            return;
        for (int x = namesBox.Items.Count - 1; x >= 0; --x)
            if (namesBox.Items[x].ToString().Contains(n))
                namesBox.Items.RemoveAt(x);
    }

    public void clearText()
    {
        try
        {
            lock (conLock)
            {
                chatBox.Text = "";
            }
        }
        catch (Exception) { }
    }

    public void addNick(string n)
    {
        if (n.Contains(" ")) //No Spaces... such a headache
            return;
        if (n.Contains(":"))
            return;
        bool shouldAdd = true;
        n = n.Trim();
        for (int x = namesBox.Items.Count - 1; x >= 0; --x)
            if (namesBox.Items[x].ToString().Contains(n))
                shouldAdd = false;
        if (shouldAdd)
        {
            namesBox.Items.Add(n);
            output("Someone new joined the room: " + n);
            //sendRaw("!!addlist " + nickName);
        }
    }

    public void addNickNoMessage(string n)
    {
        if (n.Contains(" ")) //No Spaces... such a headache
            return;
        if (n.Contains(":"))
            return;
        bool shouldAdd = true;
        n = n.Trim();
        for (int x = namesBox.Items.Count - 1; x >= 0; --x)
            if (namesBox.Items[x].ToString().Contains(n))
                shouldAdd = false;
        if (shouldAdd)
        {
            namesBox.Items.Add(n);
            //sendRaw("!!addlist " + nickName);
        }
    }

    public void getData()
    {
        try
        {
            byte[] buf = new byte[1024];
            string message = "";
            while(isConnected)
            {
                Array.Clear(buf, 0, buf.Length);
                message = "";
                int gotData = clientSocket.Receive(buf, buf.Length, SocketFlags.None);
                if (gotData == 0)
                    throw new Exception("I swear, this was working before but isn't anymore...");
                message = Encoding.ASCII.GetString(buf);
                if (message.StartsWith("!!addlist"))
                {
                    message = message.Replace("!!addlist", "");
                    string userNick = message.Trim();
                    if(!namesBox.Items.Contains(userNick))
                    {
                        addNick(userNick);
                    }
                    continue;
                }
                else if (message.StartsWith("!!removelist"))
                {
                    message = message.Replace("!!removelist", "");
                    string userNick = message.Trim();
                    removeNick(userNick);
                    output("Someone left the room: " + userNick);
                    continue;
                }
                output(message);
            }
        }
        catch (Exception)
        {
            isConnected = false;
            output(Environment.NewLine + "Connection to the server lost.");
        }
    }

    public void output(string s)
    {
        try
        {
            lock (conLock)
            {
                chatBox.Text += s + Environment.NewLine;
            }
        }
        catch (Exception) { }
    }

    private void ChatWindow_FormClosed(object sender, FormClosedEventArgs e)
    {
        try
        {
            if(isConnected)
                sendRaw("!!removelist " + nickName);
            isConnected = false;
            clientSocket.Shutdown(SocketShutdown.Receive);
            if (chatThread.IsAlive)
                chatThread.Abort();
            callingForm.Close();
        }
        catch (Exception) { }
    }

    private void sendButton_Click(object sender, EventArgs e)
    {
        if(isConnected)
            send(sendBox.Text);
    }

    private void sendBox_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter)
        {
            if (isConnected)
            {
                if (sendBox.Text != "")
                {
                    send(sendBox.Text);
                    sendBox.SelectAll();
                    e.SuppressKeyPress = true;
                    e.Handled = true;
                }
            }
        }
    }

    private void send(string t) {
        try
        {
            byte[] data = System.Text.Encoding.ASCII.GetBytes(nickName + ": " + t + "\r\n");
            clientSocket.Send(data);
            output(nickName + ": " + t);
        }
        catch (Exception e)
        {
            displayError(e.ToString());
        }
    }

    private void sendRaw(string t)
    {
        try
        {
            byte[] data = System.Text.Encoding.ASCII.GetBytes(t + "\r\n");
            clientSocket.Send(data);
        }
        catch (Exception e)
        {
            displayError(e.ToString());
        }
    }

    private void chatBox_TextChanged(object sender, EventArgs e)
    {
        chatBox.SelectionStart = chatBox.Text.Length;
        chatBox.ScrollToCaret();
    }

    private void sendBox_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter)
            e.SuppressKeyPress = true;
    }
}

要做到:

添加调用,更多的代表,做一些更多的QA,并找出打破它。
另外,我相信还是有丢包的可能性,由于客户端addlist功能,在读取循环之中。我相信这就是为什么蹩脚黑客服务器回调名人口使用Thread.sleep代码(10)是一个问题。

Add invokes, more delegates, do some more QA and find out what breaks it. Also, I believe there's still the possibility of packet loss due to the client addlist functions being in the read loop. I believe this is why the "crappy hack" using Thread.Sleep(10) in the server callback for name population is an issue.

我想这可能是更好的为pass命令给另一个线程同时继续阅读或在客户端告诉服务器它已经准备好了另一个名字。

I think it might be better to either pass the command off to another thread while continuing to read or have the client tell the server it's ready for another name.

否则,有可能是在名称更新一些数据丢失。

Otherwise, there might be some data loss during name updates.

另一件事是,如上面的评论有人说,与会代表的的更新UI对象(客舱和列表框)时使用。我写了code对于这些,但最终删除它,因为没有明显的变化,我想保持简单。

The other thing is that, as was said in the comments above, delegates should be used when updating the UI objects (chatbox and listbox). I wrote the code for these but ultimately removed it because there was no noticeable change and I wanted to keep it simple.

我不输出文本到客舱时,仍使用对象的锁,但没有明显的区别在那里。

I do still use an object lock when outputting text to the chatbox, but there's no noticeable difference there.

在code的的添加为不使用代理是潜在的问题,但我硬是抓住了聊天框在更新没有问题的无限循环。

The code should be added as not using delegates is potentially problematic, but I literally caught the chat box in an infinite loop of updates without issue.

我试图通过telnet打破它,是成功的,所以我增加了一个newConnection属性添加到StateObject以确保每个客户端只能发送!addlist一次。

I tried breaking it with telnet and was successful so I added a newConnection property to the StateObject to ensure that each client can only send "!!addlist" once.

有,当然,其他方式通过创建一个客户端加入和离开一再滥用服务器,所以最终我可能会最终通过了!! removelist处理的服务器,而不是留给了客户端。

There are, of course, other ways to abuse the server by creating a client that joins and leaves repeatedly, so ultimately I will probably end up passing the !!removelist handling to the server instead of leaving it up to the client.

这篇关于异步聊天服务器缓冲区问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆