Так что я очень заинтересован в изучении более сложных техник и языков программирования, и я решил продолжить свое изучение C #, изучая сокеты и серверное программирование. Я наткнулся на видео , которое, держу пари, многие из вас уже видели или слышали, и, на мой взгляд, довольно хорошо объясняет большую часть происходящего.
Так что моя проблема, в частности, заключается в том, чтобы по сути взять этот код и сделать его системой чата, которая получает информацию от клиента, отправляет ее на сервер, а затем с сервера на ВСЕ клиенты. Я просмотрел все учебники и другие страницы переполнения стека, но безрезультатно, по крайней мере, для себя. Я не могу понять это, и когда у кого-то есть ответ, он просто пишет «Нашел решение» и более или менее оставляет это при этом.
Мне известно о этой ветке на SO, и я пробовал то, что было там опубликовано, но безрезультатно. Я также сделал свою честную долю поиска через 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.
РЕДАКТИРОВАТЬ : код, который я использовал для моего клиента, находится по этой ссылке: https://pastebin.com/4WicEu2x
РЕДАКТИРОВАТЬ 2 : У меня такое чувство, что главная проблема заключается в том, что в моем клиентском коде клиент прослушивает только сообщения ПОСЛЕ отправляет сообщение. Если это так, то я тупее, чем скала, и хотел бы поблагодарить всех, кто писал здесь, рассказывая мне, как улучшить мою программу.
РЕДАКТИРОВАТЬ 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
для приема данных, отправленных сервером. Всегда пересматривайте свой код и логику!