C#发布从服务器向多个客户端发送消息(套接字编程)
所以我对学习更复杂的编程技术和语言非常感兴趣,我决定通过学习sockets和服务器编程进一步学习C。我偶然发现了一个我敢打赌你们很多人已经看到或听说过的故事,在我看来,它非常善于解释大部分正在发生的事情 所以我的问题是,从本质上讲,这段代码是一个聊天系统,它从客户端获取信息,发送到服务器,然后从服务器发送到所有客户端。我查过任何教程和其他堆栈溢出页面都没有用,至少对我来说是这样。我似乎不明白,当有人有答案时,他们只是简单地写下“找到了解决方案”,然后或多或少地把它放在一边 我知道这是一个很好的帖子,我试过贴在上面的东西,但都没用。我也通过谷歌和诸如此类的网站完成了我的搜索任务,由于我对sockets和所有这些都不熟悉,这绝对不是一件容易的事情 实际问题 以下是我的服务器代码:C#发布从服务器向多个客户端发送消息(套接字编程),c#,sockets,C#,Sockets,所以我对学习更复杂的编程技术和语言非常感兴趣,我决定通过学习sockets和服务器编程进一步学习C。我偶然发现了一个我敢打赌你们很多人已经看到或听说过的故事,在我看来,它非常善于解释大部分正在发生的事情 所以我的问题是,从本质上讲,这段代码是一个聊天系统,它从客户端获取信息,发送到服务器,然后从服务器发送到所有客户端。我查过任何教程和其他堆栈溢出页面都没有用,至少对我来说是这样。我似乎不明白,当有人有答案时,他们只是简单地写下“找到了解决方案”,然后或多或少地把它放在一边 我知道这是一个很好的帖
using static AppNameHere.ChatCommands;
namespace AppNameHere
{
public partial class Host : Form
{
public static Host host = null;
private static string response;
private static byte[] _buffer = new byte[1024];
private static List<Socket> _ClientSk = new List<Socket>();
private static Socket _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public Host()
{
InitializeComponent();
}
private void Host_Load(object sender, EventArgs e)
{
SetupServer();
}
private static void SetupServer()
{
_Socket.Bind(new IPEndPoint(IPAddress.Any, HostJoinSelect.portSelected));
_Socket.Listen(HostJoinSelect.playerTotal + 2);
_Socket.BeginAccept(new AsyncCallback(AccCallback), null);
}
private static void AccCallback(IAsyncResult iar)
{
Socket s = _Socket.EndAccept(iar);
_ClientSk.Add(s);
Console.WriteLine("Client Connected.");
s.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(RecCallback), s);
_Socket.BeginAccept(new AsyncCallback(AccCallback), null);
}
private static void RecCallback(IAsyncResult iar)
{
Socket s = (Socket)iar.AsyncState;
int received = s.EndReceive(iar);
byte[] dataBuf = new byte[received];
Buffer.BlockCopy(_buffer, 0, dataBuf, 0, received);
string t = Encoding.ASCII.GetString(dataBuf);
foreach (Socket socket in _ClientSk)
{
response = ChatCommandParser(t);
byte[] data = Encoding.ASCII.GetBytes(response);
socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket);
socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(RecCallback), socket);
}
}
private static void SendCallback(IAsyncResult iar)
{
Socket s = (Socket)iar.AsyncState;
s.EndSend(iar);
}
}
}
我将代码从仅在它自己发送消息之后才接受消息更改为使用异步
BeginReceive
接收服务器发送的数据。始终检查您的代码和逻辑 除了您的代码需要一些修复之外,它仍然有效。我在我的测试项目中执行了它,它按照您的要求工作(几乎没有并发问题)
所需的并发修复程序:
\u ClientSk.Add(s)
调用周围使用锁{}
,或b)使用一般来说,代码还有很多工作要做。例如,您应该处理客户端断开连接。目前,只要您的客户端决定关闭连接,它就会在
int received=s.EndReceive(iar)
调用时抛出异常。除了处理异常之外,您还需要向协议中添加某种方式来执行优雅的连接关闭。例如,通过处理EOF符号。您将从MSFT中找到一个不错的代码示例。您是否尝试过调试并查看列表中的实际内容?什么被送到哪里?另外,您可能不想在循环中BeginReceive
,也不想对每个客户端使用相同的缓冲区。@SamiKuhmonen我已经调试过,在列表中看到,如果连接了x个客户端,列表的长度将是x,for循环将运行x次,这是应该的。至于BeginReceive
你能解释一下为什么它不应该在循环中吗?谢谢你的回复!我知道我在某些方面有所欠缺,比如关闭连接,但我正试图一次解决一个问题。我不太明白你们在这里得到了什么,但从我能拼凑的东西来看,这可能是完全错误的,这个程序不应该是异步的?我也不知道这对你有什么作用,但对我没有作用。你的客户长什么样?这是我的:重新发布评论,因为我以前编辑它花了太长时间。所以我在客户端找到了解决方案,显然我太笨了,看不到客户端代码。尽管如此,还是非常感谢您的帮助,让我能够深入研究我的代码。另外,我将很快应用你提出的许多建议继续学习。非常感谢!我将在根帖子中对我的解决方案进行编辑。@EmirCurtovic,祝你的项目好运!关于客户端:我刚刚使用了-d blahblah
开关,它有点笨拙,但可以完成以下任务:)。此外,您的程序应该是异步的,因为它服务于多个客户端,但是对类成员的访问必须同步。例如,当两个线程同时向同一个列表添加元素时,可能会导致非常糟糕的情况。
namespace AppNameHere
{
public partial class Join : Form
{
public static Join join = null;
private static Socket _ClientSk = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private static byte[] recBuf = new byte[1024];
public Join()
{
InitializeComponent();
}
private void Join_Load(object sender, EventArgs e)
{
}
private void Join_FormClosed(object sender, FormClosedEventArgs e)
{
if (HostJoinSelect.hjs != null)
{
HostJoinSelect.hjs.Show();
}
else
{
HostJoinSelect.hjs = new HostJoinSelect();
HostJoinSelect.hjs.Show();
}
}
private void Join_Shown(object sender, EventArgs e)
{
LoopConnect();
}
private static void Send()
{
string req = join.textBox1.Text;
byte[] buffer = Encoding.ASCII.GetBytes(req);
_ClientSk.Send(buffer);
}
private static void ConnectedCallback()
{
_ClientSk.BeginReceive(recBuf, 0, recBuf.Length, SocketFlags.None, new AsyncCallback(ReceivedCallback), _ClientSk);
}
private static void ReceivedCallback(IAsyncResult iar)
{
Socket s = (Socket)iar.AsyncState;
int rec = s.EndReceive(iar);
byte[] dataBuf = new byte[rec];
Buffer.BlockCopy(recBuf, 0, dataBuf, 0, rec);
string q = Encoding.ASCII.GetString(dataBuf);
join.Invoke(new MethodInvoker(delegate () {
join.listBox1.Items.Add(Form1.namesave + ": " + q);
join.listBox1.TopIndex = join.listBox1.Items.Count - 1;
}));
s.BeginReceive(recBuf, 0, recBuf.Length, SocketFlags.None, new AsyncCallback(ReceivedCallback), s);
}
private static void LoopConnect()
{
int attempts = 0;
while (!_ClientSk.Connected)
{
if (attempts < 4)
{
try
{
attempts++;
if (attempts <= 4)
{
_ClientSk.Connect(HostJoinSelect.IPSelectedJoin, HostJoinSelect.portSelectedJoin);
Console.WriteLine("Connected");
ConnectedCallback();
}
else
{
attempts = 0;
break;
}
}
catch (SocketException s)
{
if (attempts <= 4)
{
Console.WriteLine(s.Message + " | Connection attempts: " + attempts.ToString());
}
else
{
attempts = 0;
break;
}
}
}
else
{
MessageBox.Show("You failed to connect to " + HostJoinSelect.IPSelectedJoin + ":" + HostJoinSelect.portSelectedJoin + ", please ensure you have a means of connecting to this address.");
join.Close();
break;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
Send();
}
}
}