我正在使用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不是问题所在。我已经测试了。
答案 0 :(得分:1)
当我将代码复制到新项目时,我无法编译代码,所以我不确定。但有一件事是肯定的,你必须在这里发生许多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
}
接下来,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
,并处理异步接收客户端消息数据。这使用BeginInvoke
和EndInvoke
异步模式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的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线程。您也可以抽象出这一点,以便Server
和ConnectionState
对象始终推送给您提供的调度程序。这减少了您必须执行的Invoke(Action)
次调用次数。
这适用于多个连接。我启动了服务器,然后在几台不同的计算机上启动了两个不同的连接而没有任何问题。您需要自定义数据处理并将消息推送到客户端。这应该至少可以解决您的多连接问题,并减少对CPU的压力(不再需要循环!)。
希望这会有所帮助。