因此,我对学习更复杂的编程技术和语言非常感兴趣,因此,我决定通过学习套接字和服务器编程来进一步研究C#。我偶然发现video是我打赌你们很多人已经看到或听说过的,而且我认为它非常擅长于解释大多数正在发生的事情。
因此,我的问题主要是从本质上采用此代码,并使其成为一个聊天系统,该系统从客户端获取信息,将其发送到服务器,然后从服务器发送到所有客户端。我查找了任何教程和其他堆栈溢出页面都没有用,至少对我来说没有用。我似乎无法弄清楚,当有人得到答案时,他们只是写了“找到解决方案”,或多或少地留了下来。
我知道SO上有this线程,因此我尝试了其中的发布,但无济于事。我还完成了相当一部分的搜索工作,即通过google和诸如此类的东西进行搜索,并且由于不熟悉套接字等所有功能,因此绝对不是最简单的事情。
这是我的服务器代码:
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);
}
}
}
顶部的using
只是我为将文本转换为其他内容而制作的一个类,因此没有障碍。真正使我感到困惑的是foreach
循环,从理论上讲,它应该起作用,并且它应该遍历所有客户端并向其发送所有消息,但如果客户端发送一条消息,其他客户端没有任何反应。只有原始客户才能收到邮件。
因此,调试和故障排除都表明它可以通过BeginSend运行,但是它仍然只向我输入的那个奇怪的客户端发送一条消息。
请先谢谢您,如果您有答案,请向我解释,因为我仍在学习,因此进行修复和作出解释将是不错的选择。
P.S。是的,我知道我使用的是TCP,而不是UDP。
编辑:我用于客户端的代码位于以下pastebin链接中:https://pastebin.com/4WicEu2x
编辑2 :我觉得主要的问题是,在我的客户代码中,我让客户仅监听消息 AFTER 它发送一条消息。如果是这样,那么我比石头更笨,要感谢任何在这里发布内容并告诉我如何改进程序的人。
编辑3 :我在第二次编辑中写的是正确的。如果您遇到问题,并且已经按照我在该youtube视频中所做的相同指南进行操作,那么只需知道您的客户端程序和服务器中都应具有异步接收功能。我对客户端没有太多的想法,但是请务必检查您的代码!这是我编辑的客户端代码(是的,我知道它需要很多工作):
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();
}
}
}
我将代码从仅在自身发送消息之后才接受消息更改为使用异步BeginReceive
来接收服务器发送的数据。始终检查您的代码和逻辑!
答案 0 :(得分:0)
除了您的代码需要一些修复的事实外,它仍然有效。我在测试项目中执行了它,并且按您的要求进行了工作(几乎没有并发问题)。
所需的并发修复程序:
logcat
调用周围使用lock {}
或b)使用concurrent collections。如果客户端连接同时出现,则最后一个错误可能会导致您描述的行为。但是,您的客户端代码也可能会出现一些问题。如果在引入修复程序后仍然遇到问题,请同时发布您的客户端代码。
通常,您的代码还有更多工作要做。例如,您应该处理客户端断开连接。当前,只要您的客户端决定关闭连接,它只会在_ClientSk.Add(s)
调用中引发异常。除了处理异常之外,您还需要向协议添加某种方式来执行正常的连接关闭。例如,通过处理EOF符号。 Here您将从MSFT中找到一个不错的代码示例。