c# - 当另一个客户端加入时,客户端的Socket.send不能正常工作

标签 c# winforms sockets

我正在使用C#Winform中的套接字编写聊天程序。
它适用于单个客户端,但是当我打开另一个客户端时,以前的客户端(运行良好)的发送功能将永远无法正常工作,但会继续接收数据。

这是我的整个源代码(因为idk出了什么问题,我得到一些建议,为他们提供了一些较差的细节)

服务器:

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

namespace Player__Server_
{
    public partial class Form1 : Form
    {
        Socket Serv;
        List<Socket> ClnSocket = new List<Socket>();
        List<string> MusList = new List<string>();
        List<string> Nickname = new List<string>();

        int ClnCounter = 0;
        int MusCounter = 0;
        int BufferingCount = 0;
        Socket socket;
        Thread run;
        private delegate void sDelegate(string sData, int socketIndex);
        public Form1()
        {
            InitializeComponent();
        }

        new public string Left(string Text, int TextLength)
        {
            if (Text.Length < TextLength)
            {
                TextLength = Text.Length;
            }
            return Text.Substring(0, TextLength);
        }

        new public string Right(string Text, int TextLength)
        {
            if (Text.Length < TextLength)
            {
                TextLength = Text.Length;
            }
            return Text.Substring(Text.Length - TextLength, TextLength);
        }

        new public string Mid(string sString, int nStart, int nLength)
        {
            string sReturn;
            --nStart;

            if (nStart <= sString.Length)
            {
                if ((nStart + nLength) <= sString.Length)
                {
                    sReturn = sString.Substring(nStart, nLength);
                }
                else
                {
                    sReturn = sString.Substring(nStart);
                }
            }
            else
            {
                sReturn = string.Empty;
            }
            return sReturn;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // prevent exception
            Stream fe = new FileStream(Environment.CurrentDirectory + "/Music.server", FileMode.OpenOrCreate);
            fe.Close();

            // Read "Music.server" file and add to MusList (List)
            string line;
            StreamReader fr = new StreamReader(Environment.CurrentDirectory + "/Music.server");
            while ((line = fr.ReadLine()) != null)
            {
                MusList.Add(line);
                MusCounter++;
            }
            fr.Close();

            // prevent exception
            Stream fa = new FileStream(Environment.CurrentDirectory + "/Account.server", FileMode.OpenOrCreate);
            fa.Close();

            Serv = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            Serv.Bind(new IPEndPoint(IPAddress.Any, 9180));
            Serv.Listen(100);

            Thread accept = new Thread(this.Accept);
            accept.Start();
        }

        private void runChatting(object s)
        {
            byte[] str = new byte[2048];
            socket = s as Socket;
                while (true)
                {
                    try
                    {
                        str = new byte[2048];
                        socket.Receive(str);
                        sDelegate sdelegate = new sDelegate(this.Receive);
                        this.Invoke(sdelegate, Encoding.Default.GetString(str), ClnSocket.IndexOf(socket));
                    }
                    catch
                    {
                        try
                        {
                            ClnSocket.Remove(socket);
                            ClnCounter--;
                        }
                        catch { }
                        return;
                    }


                }
        }

        private string GetMusic(string MusName)
        {
            // Function :: return original information of music- search by name

            int i;
            for (i = 0; i < MusCounter; i++)
            {
                try
                {
                    if (MusList[i].IndexOf(MusName) > 0)
                    {
                        return MusList[i];
                    }
                }
                catch { }
            }
            return null;
        }

        private void Receive(string sData, int socketIndex)
        {
            TextBox.AppendText("GET : " + sData);
            if (sData.IndexOf(Environment.NewLine) > 0) { ; } else TextBox.AppendText(Environment.NewLine);
            sData = sData.Replace("\0", "");
            if (Left(sData,10) == "#musicadd#")
            {
                string TempData = Mid(sData, 11, sData.Length);
                string[] SpliteData = TempData.Split('#');
                if (GetMusic(SpliteData[1]) == null)
                {
                    Stream fs = new FileStream(Environment.CurrentDirectory + "/Music.server", FileMode.Append);
                    StreamWriter ws = new StreamWriter(fs);
                    ws.WriteLine(SpliteData[0] + "#" + SpliteData[1] + "#" + SpliteData[2] + "#sc");
                    ws.Close();
                    fs.Close();

                    MusList.Add(SpliteData[0] + "#" + SpliteData[1] + "#" + SpliteData[2] + "#sc");
                    MusCounter++;
                }
                SendTo("#musicadd#" + SpliteData[1], socketIndex);
            }
            else if (Left(sData, 7) == "#login#")
            {
                SendAll(Mid(sData, 8, sData.Length) + " Connected." + Environment.NewLine);
            }
            else if (Left(sData, 14) == "#requestmusic#")
            {
                string requestValue = GetMusic(Mid(sData, 15, sData.Length));
                SendAll("#buffermusic#" + requestValue);
                BufferingCount = 0;
            }
            else if (Left(sData, 12) == "#bufferdone#")
            {
                BufferingCount++;
                if (BufferingCount == ClnCounter)
                {
                    SendAll("#musicplay#");
                }
            }
            else
            {
                SendAll(sData);
            }
        }

        private void SendAll(string sData)
        {
            int i;
            for (i = 0; i < ClnSocket.Count; i++)
            {
                try
                {
                    ClnSocket[i].Send(Encoding.Default.GetBytes(sData));
                }
                catch { }
            }
            TextBox.AppendText("POST : " + sData);
            if (sData.IndexOf(Environment.NewLine) > 0) { ; } else TextBox.AppendText(Environment.NewLine);
        }

        private void SendTo(string sData, int socketIndex)
        {
            try
            {
                ClnSocket[socketIndex].Send(Encoding.Default.GetBytes(sData));
                TextBox.AppendText("POST TO (" + socketIndex.ToString() + ") : " + sData);
                if (sData.IndexOf(Environment.NewLine) > 0) { ; } else TextBox.AppendText(Environment.NewLine);
            }
            catch { }
        }
        private void Accept()
        {
                while (true)
                {
                    ClnSocket.Add(Serv.Accept());
                    ClnCounter++;
                    run = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(this.runChatting));
                    run.Start(ClnSocket[ClnCounter - 1]);
                }
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.Serv.Close();
            this.Serv = null;
            for (int i = 0; i < this.ClnSocket.Count; i++)
            {
                this.ClnSocket[i].Close();
            }
            this.ClnSocket.Clear();
            System.Environment.Exit(0);
        }
    }
}

客户:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Media;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.IO;

namespace MineSky_Player
{
    public partial class Form1 : Form
    {
        WMPLib.WindowsMediaPlayer Player = new WMPLib.WindowsMediaPlayer();
        public Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        Thread run;
        string BufferingInformation;
        int current_min = 0, current_sec = 0;
        int duration_min = 0, duration_sec = 0;
        int MusCounter = 0;

        public string mynick { get; set; }
        string MyNick;

        List<string> MusList = new List<string>();

        public delegate void sDelegate(string sData);
        public Form1()
        {
            try
            {
                InitializeComponent();
                socket.Connect("localhost", 9180);
                run = new Thread(new ParameterizedThreadStart(Run));
                run.Start();
            }
            catch (Exception ex){
                MessageBox.Show(ex.ToString());
                System.Environment.Exit(0);
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // prevent exception
            Stream fe = new FileStream(Environment.CurrentDirectory + "/Music.client", FileMode.OpenOrCreate);
            fe.Close();

            // Read "Music.client" file and add to MusList (List)
            string line;
            StreamReader fr = new StreamReader(Environment.CurrentDirectory + "/Music.client");
            while ((line = fr.ReadLine()) != null)
            {
                MusList.Add(line);
                MusCounter++;
                MusicList.Items.Add(line);
            }
            fr.Close();

            MyNick = mynick;
        }

        new public string Left(string Text, int TextLength)
        {
            if (Text.Length < TextLength)
            {
                TextLength = Text.Length;
            }
            return Text.Substring(0, TextLength);
        }

        new public string Right(string Text, int TextLength)
        {
            if (Text.Length < TextLength)
            {
                TextLength = Text.Length;
            }
            return Text.Substring(Text.Length - TextLength, TextLength);
        }

        new public string Mid(string sString, int nStart, int nLength)
        {
            string sReturn;
            --nStart;

            if (nStart <= sString.Length)
            {
                if ((nStart + nLength) <= sString.Length)
                {
                    sReturn = sString.Substring(nStart, nLength);
                }
                else
                {
                    sReturn = sString.Substring(nStart);
                }
            }
            else
            {
                sReturn = string.Empty;
            }
            return sReturn;
        }
        private void BufferTick_Tick(object sender, EventArgs e)
        {
            if (Player.playState.ToString() == "wmppsPlaying")
            {
                Player.controls.stop();
                ToSocket("#bufferdone#");
                BufferTick.Enabled = false;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Player.controls.play();
        }

        private void Run(object s)
        {
            byte[] str = new byte[2048];
            try
            {
                while (true)
                {
                    str = new byte[2048];
                    socket.Receive(str);
                    sDelegate sdelegate = new sDelegate(this.Receive);
                    IntPtr x;
                    if (!this.IsHandleCreated) x = this.Handle;
                    this.Invoke(sdelegate, Encoding.Default.GetString(str));
                }
            }
            catch (Exception e)
            {
                MessageBox.Show("Connection Lost." + Environment.NewLine + e.ToString());
                Application.Exit();
            }
        }

        public void Receive(string sData)
        {
            //MessageBox.Show("GET : " + sData);
            sData = sData.Replace("\0", "");
            if (Left(sData, 10) == "#musicadd#")
            {
                if (MusicList.Items.Contains(Mid(sData, 11, sData.Length)))
                {
                    MessageBox.Show("Already in the list!", "MSPlayer", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
                else
                {
                    Stream fs = new FileStream(Environment.CurrentDirectory + "/Music.client", FileMode.Append);
                    StreamWriter ws = new StreamWriter(fs);
                    ws.WriteLine(Mid(sData, 11, sData.Length));
                    ws.Close();
                    fs.Close();
                    MusList.Add(Mid(sData, 11, sData.Length));
                    MusicList.Items.Add(Mid(sData, 11, sData.Length));
                    MusCounter++;
                }
            }
            else if (Left(sData, 13) == "#buffermusic#")
            {
                PlayProgressBar.Value = 0;
                current_min = 0;
                current_sec = 0;
                Player.URL = "";
                BufferingInformation = Mid(sData, 14, sData.Length);
                playingLabel.Text = BufferingInformation.Split('#')[1];
                playLabel.Text = "Buffering...";
                Player.URL = "https://player.soundcloud.com/player.swf?url=https%3A//api.soundcloud.com/tracks/" + BufferingInformation.Split('#')[0] + ";color=ff5500&show_comments=false&auto_play=true& color=a2eeff";
                BufferTick.Enabled = true;
            }
            else if (Left(sData, 11) == "#musicplay#")
            {
                duration_min = Int32.Parse(BufferingInformation.Split('#')[2]) / 60;
                duration_sec = Int32.Parse(BufferingInformation.Split('#')[2]) % 60;
                playLabel.Text = "0:00 / " + duration_min.ToString() + ":" + duration_sec.ToString();
                PlayProgressBar.Maximum = Int32.Parse(BufferingInformation.Split('#')[2]);
                Player.controls.play();
                PlayTick.Enabled = true;
            }
            else
            {
                Text_Board.AppendText(sData.Replace("\#\", "#"));
            }
        }

        public void Send(string sData)
        {
            sData = sData.Replace("#", "\#\");
            Send_Supplied(sData);
        }

        public void Send_Supplied(string sData)
        {
            if (Left(sData, 2) == "//")
            {
                sData = sData.ToLower();
                if (Left(sData, 6) == "//exit") Application.Exit();
            }
            else
            {
                ToSocket(sData);
            }
        }

        private void ToSocket(string sData)
        {
            socket.Send(Encoding.Default.GetBytes(sData));
        }
        private void Text_Chat_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                string str = this.Text_Chat.Text.Replace(Environment.NewLine, "");
                this.Text_Chat.Text = "";
                Send(MyNick + " : " + str + Environment.NewLine);
            }
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            socket.Close();
            System.Environment.Exit(0);
        }

        private void Button_Exit_Click(object sender, EventArgs e)
        {
            socket.Close();
            System.Environment.Exit(0);
        }

        private void MusicList_DoubleClick(object sender, EventArgs e)
        {
            ToSocket("#requestmusic#" + MusicList.GetItemText(MusicList.SelectedItem));
        }

        private void Button_MusicAdd_Click(object sender, EventArgs e)
        {
            AddForm addform = new AddForm();
            addform.socket = socket;
            addform.ShowDialog();
        }

        private void MusicList_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void PlayTick_Tick(object sender, EventArgs e)
        {
            if (Player.playState.ToString() == "wmppsPlaying")
            {
                current_sec++;
                PlayProgressBar.Value++;
                if (current_sec == 60)
                {
                    current_min++;
                    current_sec = 0;
                }

                playLabel.Text = current_min.ToString() + ":" + current_sec.ToString() + " / " + duration_min.ToString() + ":" + duration_sec.ToString();
                if (PlayProgressBar.Value == PlayProgressBar.Maximum)
                {
                    Player.controls.stop();
                    playingLabel.Text = "Not Playing";
                    playLabel.Text = "0:00 / 0:00";
                    PlayProgressBar.Value = 0;
                    PlayTick.Enabled = false;
                }
            }
        }
    }
}

我认为,Addform和Enterform并不是问题。我测试过了

最佳答案

当我将您的代码复制到新项目中时,我无法对其进行编译,因此我不确定。可以肯定的是,这里必须发生许多while循环。我建议用async pattern类可用的Socket替换那些。它将删除所有的while循环。一点额外的抽象也会有所帮助。

我将服务器分为自己的类,然后为每个Socket连接提供一个包装类,服务器将对该包装类进行引用。

请注意,我使用的是Visual Studio 2015,因此我的字符串格式使用的是新的C#6.0样式。如果您不能使用C#6,那么很容易将其转换回string.Format("{0}", foo);
服务器状态

我们需要一种检查当前服务器状态是什么的方法。这可以像一个小的enum一样简单。

/// <summary>
/// Provides status values for the server to use, indicating its current state.
/// </summary>
public enum ServerStatus
{
    /// <summary>
    /// The server has stopped.
    /// </summary>
    Stopped,

    /// <summary>
    /// Server is in the process of starting.
    /// </summary>
    Starting,

    /// <summary>
    /// Server is up and running.
    /// </summary>
    Running
}

连接的Args

接下来,Server类需要引发一些事件,以便可以在客户端连接时以及客户端向服务器发送消息时告诉背后的Form代码。我们将提供一个名为ConnectedArgs的类作为事件处理程序参数。此类将使用下一步创建的包装器类保存对我们实际客户的引用。
public class ConnectedArgs : EventArgs
{
    /// <summary>
    /// Instances a new ConnectedArgs class.
    /// </summary>
    /// <param name="state">The state of a client connection.</param>
    public ConnectedArgs(ConnectionState state)
    {
        this.ConnectedClient = state;
    }

    /// <summary>
    /// Gets the client currently connected.
    /// </summary>
    public ConnectionState ConnectedClient { get; private set; }
}

连接状态

此类负责保存与连接的客户端关联的Socket,并负责异步接收客户端消息数据。这使用了BeginInvokeEndInvoke异步模式included with the Socket

此类将有一个事件,该事件将用于通知Form收到新消息。请注意,这是从我现有的项目之一中提取的,因此数据解析基本上会检查缓冲区,如果缓冲区中不包含\r\n,则认为该缓冲区不完整。它缓存它,并等待来自客户端的下一个数据块进行处理并尝试并完成。您将需要用处理处理接收到的数据的自定义方法替换ProcessReceivedData方法。完成后,只需将结果推送到OnDataReceived方法中,就可以为其提供Form了。
public sealed class ConnectionState
{
    /// <summary>
    /// The size of the buffer that will hold data sent from the client
    /// </summary>
    private readonly int bufferSize;

    /// <summary>
    /// A temporary collection of incomplete messages sent from the client. These must be put together and processed.
    /// </summary>
    private readonly List<string> currentData = new List<string>();

    /// <summary>
    /// What the last chunk of data sent from the client contained.
    /// </summary>
    private string lastChunk = string.Empty;

    /// <summary>
    /// Instances a new PlayerConnectionState.
    /// </summary>
    /// <param name="player">An instance of a Player type that will be performing network communication</param>
    /// <param name="currentSocket">The Socket used to communicate with the client.</param>
    /// <param name="bufferSize">The storage size of the data buffer</param>
    public ConnectionState(Socket currentSocket, int bufferSize)
    {
        this.CurrentSocket = currentSocket;
        this.bufferSize = bufferSize;
        this.Buffer = new byte[bufferSize];
    }

    /// <summary>
    /// This event is raised when the server has received new, valid, data from the client.
    /// </summary>
    public event EventHandler<string> DataReceived;

    /// <summary>
    /// Gets the Socket for the player associated with this state.
    /// </summary>
    public Socket CurrentSocket { get; private set; }

    /// <summary>
    /// Gets the data currently in the network buffer
    /// </summary>
    public byte[] Buffer { get; private set; }

    /// <summary>
    /// Gets if the current network connection is in a valid state.
    /// </summary>
    public bool IsConnectionValid
    {
        get
        {
            return this.CurrentSocket != null && this.CurrentSocket.Connected;
        }
    }

    /// <summary>
    /// Starts listening for network communication sent from the client to the server
    /// </summary>
    public void StartListeningForData()
    {
        this.Buffer = new byte[bufferSize];
        this.CurrentSocket.BeginReceive(this.Buffer, 0, bufferSize, 0, new AsyncCallback(this.ReceiveData), null);
    }

    /// <summary>
    /// Receives the input data from the user.
    /// </summary>
    /// <param name="result">The result.</param>
    private void ReceiveData(IAsyncResult result)
    {
        // If we are no longer in a valid state, dispose of the connection.
        if (!this.IsConnectionValid)
        {
            this.CurrentSocket?.Dispose();
            return;
        }

        int bytesRead = this.CurrentSocket.EndReceive(result);
        if (bytesRead == 0 || !this.Buffer.Any())
        {
            this.StartListeningForData();
            return;
        }

        ProcessReceivedData(bytesRead);
        this.StartListeningForData();
    }

    /// <summary>
    /// Process the data we received from the client.
    /// </summary>
    /// <param name="bytesRead"></param>
    private void ProcessReceivedData(int bytesRead)
    {
        // Encode our input string sent from the client
        this.lastChunk = Encoding.ASCII.GetString(this.Buffer, 0, bytesRead);

        // If the previous chunk did not have a new line feed, then we add this message to the collection of currentData.
        // This lets us build a full message before processing it.
        if (!lastChunk.Contains("\r\n"))
        {
            // Add this to our incomplete data stash and read again.
            this.currentData.Add(lastChunk);
            return;
        }

        // This message contained at least 1 new line, so we split it and process per line.
        List<string> messages = lastChunk.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).ToList();

        foreach (string line in this.PruneReceivedMessages(messages))
        {
            this.OnDataReceived(line);
        }
    }

    /// <summary>
    /// Runs through the messages collection and prepends data from a previous, incomplete, message
    /// and updates the internal message tracking state.
    /// </summary>
    /// <param name="messages"></param>
    private List<string> PruneReceivedMessages(List<string> messages)
    {
        // Append the first line to the incomplete line given to us during the last pass if one exists.
        if (this.currentData.Any() && messages.Any())
        {
            messages[0] = string.Format("{0} {1}", string.Join(" ", this.currentData), messages[0]);
            this.currentData.Clear();
        }

        // If we have more than 1 line and the last line in the collection does not end with a line feed
        // then we add it to our current data so it may be completed during the next pass. 
        // We then remove it from the lines collection because it can be infered that the remainder will have
        // a new line due to being split on \n.
        if (messages.Count > 1 && !messages.Last().EndsWith("\r\n"))
        {
            this.currentData.Add(messages.Last());
            messages.Remove(messages.Last());
        }

        return messages;
    }

    private void OnDataReceived(string data)
    {
        var handler = this.DataReceived;
        if (handler == null)
        {
            return;
        }

        handler(this, data);
    }
}

服务器

现在,我们已经使客户端异步接收和处理数据,我们需要编写服务器组件以实际异步地接受传入的Socket连接。

此类将有两个Form将预订的事件。一个用于客户端连接的时间,另一个用于客户端断开连接的时间。为每个连接的客户端分配一个ConnectionState,并将其缓存在List<ConnectionState>的集合中。这消除了您对连接的客户端数量保持脆弱状态的需求。

您可以选择连接一个计时器,该计时器会定期修剪List<ConnectionState>集合。您可以检查集合中的每个实例是否将其IsConnectionValid设置为true。如果返回false,则将其从集合中删除。
/// <summary>
/// The Default Desktop game Server
/// </summary>
public sealed class Server
{
    /// <summary>
    /// The user connection buffer size
    /// </summary>
    private const int UserConnectionBufferSize = 1024;

    /// <summary>
    /// The server socket
    /// </summary>
    private Socket serverSocket;

    /// <summary>
    /// The player connections
    /// </summary>
    private List<ConnectionState> connectedClients;

    /// <summary>
    /// Used for making access to the connectedClients collection thread-safe
    /// </summary>
    private object lockObject = new object();

    /// <summary>
    /// Initializes a new instance of the <see cref="Server"/> class.
    /// </summary>
    public Server()
    {
        this.Status = ServerStatus.Stopped;
        this.connectedClients = new List<ConnectionState>();
    }

    /// <summary>
    /// Occurs when a client connects to the server.
    /// </summary>
    public event EventHandler<ConnectedArgs> ClientConnected;

    /// <summary>
    /// Occurs when a client is disconnected from the server.
    /// </summary>
    public event EventHandler<ConnectedArgs> ClientDisconnected;

    /// <summary>
    /// Gets or sets the port that the server is running on.
    /// </summary>
    public int Port { get; set; }

    /// <summary>
    /// Gets or sets the maximum queued connections.
    /// </summary>
    public int MaxQueuedConnections { get; set; }

    /// <summary>
    /// Gets the current server status.
    /// </summary>
    public ServerStatus Status { get; private set; }

    public void Start()
    {
        if (this.Status != ServerStatus.Stopped)
        {
            throw new InvalidOperationException("The server is either starting or already running. You must stop the server before starting it again.");
        }
        else if (this.Port == 0)
        {
            throw new InvalidOperationException("You can not start the server on Port 0.");
        }

        this.Status = ServerStatus.Starting;

        // Get our server address information
        IPHostEntry serverHost = Dns.GetHostEntry(Dns.GetHostName());
        var serverEndPoint = new IPEndPoint(IPAddress.Any, this.Port);

        // Instance the server socket, bind it to a port.
        this.serverSocket = new Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        this.serverSocket.Bind(serverEndPoint);
        this.serverSocket.Listen(this.MaxQueuedConnections);

        // Begin listening for connections.
        this.serverSocket.BeginAccept(new AsyncCallback(this.ConnectClient), this.serverSocket);

        this.Status = ServerStatus.Running;
    }

    /// <summary>
    /// Stops the server.
    /// </summary>
    public void Stop()
    {
        this.DisconnectAll();

        // We test to ensure the server socket is still connected and active.
        this.serverSocket.Blocking = false;
        try
        {
            this.serverSocket.Send(new byte[1], 0, 0);

            // Message was received meaning it's still receiving, so we can safely shut it down.
            this.serverSocket.Shutdown(SocketShutdown.Both);
        }
        catch (SocketException e)
        {
            // Error code 10035 indicates it works, but will block the socket.
            // This means it is still receiving and we can safely shut it down.
            // Otherwise, it's not receiving anything and we don't need to shut down.
            if (e.NativeErrorCode.Equals(10035))
            {
                this.serverSocket.Shutdown(SocketShutdown.Both);
            }
        }
        finally
        {
            this.Status = ServerStatus.Stopped;
        }
    }

    /// <summary>
    /// Disconnects the specified IServerPlayer object.
    /// </summary>
    /// <param name="connection">The client to disconnect.</param>
    public void Disconnect(ConnectionState connection)
    {
        if (connection != null && connection.IsConnectionValid)
        {
            connection.CurrentSocket.Shutdown(SocketShutdown.Both);
            this.connectedClients.Remove(connection);
            this.OnClientDisconnected(connection);
        }
    }

    /// <summary>
    /// Disconnects everyone from the server.
    /// </summary>
    public void DisconnectAll()
    {
        // Loop through each connection and disconnect them.
        foreach (ConnectionState state in this.connectedClients)
        {
            Socket connection = state.CurrentSocket;
            if (connection != null && connection.Connected)
            {
                connection.Shutdown(SocketShutdown.Both);
                this.OnClientDisconnected(state);
            }
        }

        this.connectedClients.Clear();
    }

    /// <summary>
    /// Called when a client connects.
    /// </summary>
    private void OnClientConnected(ConnectionState connection)
    {
        EventHandler<ConnectedArgs> handler = this.ClientConnected;
        if (handler == null)
        {
            return;
        }

        handler(this, new ConnectedArgs(connection));
    }

    /// <summary>
    /// Called when a client disconnects.
    /// </summary>
    private void OnClientDisconnected(ConnectionState connection)
    {
        EventHandler<ConnectedArgs> handler = this.ClientDisconnected;
        if (handler == null)
        {
            return;
        }

        handler(this, new ConnectedArgs(connection));
    }

    /// <summary>
    /// Connects the client to the server and then passes the connection responsibilities to the client object.
    /// </summary>
    /// <param name="result">The async result.</param>
    private void ConnectClient(IAsyncResult result)
    {
        // Connect and register for network related events.
        Socket connection = this.serverSocket.EndAccept(result);

        // Send our greeting
        byte[] buffer = Encoding.ASCII.GetBytes("Welcome to the Music App Server!");
        connection.BeginSend(buffer, 0, buffer.Length, 0, new AsyncCallback(asyncResult => connection.EndReceive(asyncResult)), null);

        // Fetch the next incoming connection.
        this.serverSocket.BeginAccept(new AsyncCallback(this.ConnectClient), this.serverSocket);

        this.CompleteClientSetup(new ConnectionState(connection, UserConnectionBufferSize));
    }

    /// <summary>
    /// Caches the ConnectionState and has the state begin listening to client data.
    /// </summary>
    /// <param name="connectionState"></param>
    private void CompleteClientSetup(ConnectionState connectionState)
    {
        lock (this.lockObject)
        {
            this.connectedClients.Add(connectionState);
        }

        // Start receiving data from the client.
        connectionState.StartListeningForData();
        this.OnClientConnected(connectionState);
    }
}

形式

现在我们已经将服务器代码放在一起,就可以构建Form了。我只是做了一个简单的表单,有一个用于连接的按钮和一个用于查看数据的多行文本框。

我为Form的Loading事件和Button的Clicked事件添加了一个事件处理程序。
public partial class Form1 : Form
{
    private Server server;

    public Form1()
    {
        InitializeComponent();
        server = new Server { Port = 9180, MaxQueuedConnections = 100 };
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        server.ClientConnected += OnClientConnected;
        server.ClientDisconnected += OnClientDisconnected;
    }

    private void OnClientDisconnected(object sender, ConnectedArgs e)
    {
        this.Invoke(new Action(() => this.textBox1.AppendText("A Client disconnected.\n")));
    }

    private void OnClientConnected(object sender, ConnectedArgs e)
    {
        this.Invoke(new Action(() => 
        {
            this.textBox1.AppendText("New Client Connected.\n");
            e.ConnectedClient.DataReceived += OnClientSentDataToServer;
        }));
    }

    private void OnClientSentDataToServer(object sender, string e)
    {
        this.Invoke(new Action(() => this.textBox1.AppendText($"{e}\n")));
    }

    private void button1_Click(object sender, EventArgs e)
    {
        this.textBox1.AppendText("Server starting.\n");
        server.Start();
        this.textBox1.AppendText("Server running.\n");
    }
}

由于服务器作为后台进程运行,因此在访问TextBox时,必须将事件处理程序编码回UI线程。您也可以对该部分进行抽象,以便ServerConnectionState对象始终推送到您提供的调度程序中。这减少了您必须执行的Invoke(Action)调用的次数。

这适用于多个连接。我启动了服务器,然后在几台不同的计算机上建立了两个不同的连接,没有任何问题。您将需要自定义数据处理并将消息推送到客户端。这至少应该可以解决您的多连接问题,并减轻对CPU的压力(while循环不会更多!)。

希望这可以帮助。

关于c# - 当另一个客户端加入时,客户端的Socket.send不能正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30293662/

相关文章:

c# - 如何使用 EPPlus 库为 Excel 创建多样式单元格

c# - 用鼠标画矩形

java - 创建套接字连接

c# - 单击项目后 MenuStrip 不隐藏。漏洞?

c# - WPF 抛出异常解析 XAML,其中包含 Winforms 用户控件

Java 防火墙检查?告诉服务器管理员关闭防火墙

c - 在阻塞连接后设置套接字非阻塞

c# - 禁止 asp :Literal from outputing HTML

c# - 找不到 View 。

java - 接口(interface)真的可以用来实现多重继承吗