Асинхронный клиент-серверное приложение с обработкой данных
Мне нужно при подключение клиента с серверу вывести данные от клиента (IP, UserName) в ListView это у меня как бы получается, но суть в другом, данные которые поступают от клиента на сервер нужно обработать отдельно (текстовое сообщение которое отправляется от клиента на сервер)
Listview (отображает айпи и имя клиента) после из контекстного меню выбираю открыть другую форму и в другой форме есть кнопка и текстбокс, мне нужно вывести данные в другой форме от выбранного клиента те данные которые передаются от Console.ReadLine (client)
Вот код клиента:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Client
{
public static class SocketClient
{
private const int Port = 11000;
private const string ServerIp = "127.0.0.1";
public static async Task Start()
{
while (true)
{
using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
try
{
Console.WriteLine($"Попытка подключения к серверу: {ServerIp}:{Port}");
await client.ConnectAsync(ServerIp, Port);
Console.WriteLine($"Подключено к серверу: {ServerIp}:{Port}");
string userName = Environment.UserName; // Получаем имя пользователя
string ip = GetLocalIpAddress(); // Получаем локальный IP-адрес
// Формируем данные о подключившемся пользователе
string userData = $"{ip},{userName}";
byte[] userDataBytes = Encoding.UTF8.GetBytes(userData); // Кодируем данные в байты
// Отправляем данные о пользователе серверу
await client.SendAsync(new ArraySegment<byte>(userDataBytes), SocketFlags.None);
var receiveTask = Task.Run(() => ReceiveMessages(client));
await SendMessages(client);
}
catch (SocketException ex)
{
Console.WriteLine($"Ошибка подключения к серверу: {ex.Message}. Повторная попытка подключения через 3 секунды.");
await Task.Delay(3000);
}
}
}
}
private static async Task SendMessages(Socket client)
{
while (true)
{
Console.Write("Введите сообщение для сервера: ");
string message = Console.ReadLine();
byte[] data = Encoding.UTF8.GetBytes(message);
await client.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
}
}
private static async Task ReceiveMessages(Socket client)
{
byte[] buffer = new byte[1024];
while (true)
{
int bytesRead = await client.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
string data = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Получено от сервера: {data} | размер: {bytesRead} КБ");
}
}
private static string GetLocalIpAddress()
{
string localIp = string.Empty;
IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
localIp = ip.ToString();
break;
}
}
return localIp;
}
}
}
Вот код сервера:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Server
{
public partial class PanelServ : Form
{
// В PanelServ форме создайте приватное поле для хранения данных клиентов
public List<ClientData> clientDataList = new();
public int selectedClientIndex; // Индекс выбранного клиента
private Socket listener;
private Task listeningTask;
private int connectedClientsCount = 0;
public PanelServ()
{
InitializeComponent();
contextMenuStrip1.Opening += new System.ComponentModel.CancelEventHandler(contextMenuStrip1_Opening); // событие подписки на проверку контекстного меню
}
private void openLogsToolStripMenuItem_Click(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count > 0)
{
// Получение выбранного элемента ListView
ListViewItem selectedItem = listView1.SelectedItems[0];
// Сохранение индекса выбранного клиента
selectedClientIndex = selectedItem.Index;
// Открытие формы LogClientFrm
LogClientFrm log = new LogClientFrm();
log.Show();
}
}
private void contextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = listView1.Items.Count == 0; // Проверка элементов в ListView и если есть показываем контекстное меню
}
private async void ListenClientBtn_Click(object sender, EventArgs e)
{
listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Any, Convert.ToInt32(PortText.Text)));
listener.Listen(100);
InfoListen.Text = $"Listen Port Clients: {Convert.ToInt32(PortText.Text)}";
listeningTask = StartListening();
await listeningTask;
}
private async Task StartListening()
{
while (true)
{
Socket client = await listener.AcceptAsync();
InfoClient.Text = "Count clients: 0";
string ip = client.RemoteEndPoint.ToString();
string user = ""; // Здесь вы можете установить нужное значение
ListViewItem item = new(new[] { ip, user });
listView1.Items.Add(item);
connectedClientsCount++;
// Создаем экземпляр класса ClientData и заполняем его данными
ClientData clientData = new ClientData
{
IP = ip,
User = user
};
// Добавляем данные клиента в список
clientDataList.Add(clientData);
InfoClient.BeginInvoke(new Action(() =>
{
InfoClient.Text = $"Count clients: {connectedClientsCount}";
}));
await ReceiveDataFromClient(client, item, clientData);
}
}
private async Task ReceiveDataFromClient(Socket client, ListViewItem item, ClientData clientData)
{
try
{
byte[] buffer = new byte[1024];
while (true)
{
int bytesRead = await client.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
string data = Encoding.UTF8.GetString(buffer, 0, bytesRead);
string[] userData = data.Split(','); // Разделяем полученные данные на IP-адрес и имя пользователя
if (userData.Length >= 3)
{
string ip = userData[0];
string user = userData[1];
string message = userData[2];
listView1.BeginInvoke(new Action(() =>
{
item.SubItems[0].Text = ip;
item.SubItems[1].Text = user;
}));
clientData.Message = message;
}
}
}
catch (Exception)
{
// Console.WriteLine($"Ошибка при обработке клиента: {ex.Message}");
}
finally
{
client.Shutdown(SocketShutdown.Both);
client.Close();
listView1.BeginInvoke(new Action(() =>
{
listView1.Items.Remove(item);
}));
connectedClientsCount--; // Уменьшаем счетчик подключенных клиентов
// Обновляем счетчик в label
InfoClient.BeginInvoke(new Action(() =>
{
InfoClient.Text = $"Подключёно клиентов: {connectedClientsCount}";
}));
}
}
}
public class ClientData
{
public string IP { get; set; }
public string User { get; set; }
public string Message { get; set; } // тут нужны данные которые поступают от клиента (Console.ReadLine) и в дальнейшем можно будет вывести данные в текстбокс.
}
}
Код сервера (отдельной формы):
using System;
using System.Windows.Forms;
namespace Server
{
public partial class LogClientFrm : Form
{
private PanelServ parentForm; // Ссылка на родительскую форму (PanelServ)
public LogClientFrm()
{
InitializeComponent();
// Получение ссылки на родительскую форму
parentForm = (PanelServ)Owner;
}
private void button1_Click(object sender, EventArgs e)
{
// Получение данных от выбранного клиента
ClientData selectedClientData = parentForm.clientDataList[parentForm.selectedClientIndex];
// Отображение данных в текстовом поле
textBox1.Text = selectedClientData.Message;
}
}
}
P.S: Нужно именно по Socket (не TCP)
Какие вообще идеи предложите по данной реализации.
Желательно с комментариями по коду, чтобы было понятно что как и где.
Проект на .NetFramework 4.7.2
