Четверг, 03.07.2025, 22:44

  Сайт проекта StreamLive 216.73.216.50

Вы вошли как Гость | Группа "Гости" | RSS

Меню

Категории
С# [55]
VB.net [0]
VB6 [1]

Реклама

Nothing

 
Главная » Статьи » Программирование » С# [ Добавить статью ]
Работа с сокетами в .Net и C#, создание чата
Cегодня мы с Вами поговорим о программировании сокетов, с помощью платформы .Net и языка C#

Здравствуйте, сегодня мы с Вами поговорим о программировании сокетов, с помощью платформы .Net и языка C#.

Давайте разберемся, что же такое сокет?



Сокет — конечная точка связи двустороннего канала между 2 компьютерами.

Если мы соединим 2 сокета, то получим канал, через который можно передавать данные в обе стороны. Одна сторона канала называется сервером, другая — клиентом.





Для передачи/приема данных нужно открыть канал. Вконце всех операций — закрыть.
Типы сокетов

Существует 2 вида сокетов: потоковые, дейтаграммные.

Теперь о каждом по-отдельности.



Потоковый сокет — это сокет, который состоит из потока байтов, который может быть двунапрямленным (в обе стороны). Он берет на себя всю ответственность о доставке данных и исправлении ошибок.

Особенностью есть возможность передачи больших объемов данных.

Использует протокол TCP (Transmission Control Protocol), именно который обеспечивает поступление данных на другую сторону в нужной последовательности и без ошибок.

Если вам важна точность доставки данных, или их объем — потоковые сокеты будут лучшим выбором.



Дейтаграммный сокет — в отличие от потокового, имеет ограничения по размеру. Реилизирован через протокол UDP(User Datagram Protocol), который не отвечает за приход в конечную точку всех данных. Одним из плюсов — не нужно создавать соединения между 2 сторонами. Это очень важно, когда затраты времени неприпустимы.

Сокеты связываются между собой через порты.
http://ru.wikipedia.org/wiki/Порт_(IP)


Вот схема сокета:


От слова к делу

Мы создадим приложение-чат, которое реализовывает обе структуры: клиент/сервер. Для начала, нам нужно написать класс со статистическими методами для работы с сокетами.

Добавьте в проект новый класс, с именем: SocketWorker.

Вот его код:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets; // Для работы с сокетами нужно подключить это пространство имен
using System.Windows.Forms;


namespace TestChat
{
class SocketWorker
{
// ----------------------------------------------------------------------
private static IPHostEntry ipHost; // Класс для сведений об адресе веб-узла
private static IPAddress ipAddr; // Предоставляет IP-адрес
private static IPEndPoint ipEndPoint; // Локальная конечная точка

private static Socket Server; // Создаем объект сокета-сервера
private static Socket Client; // Создаем объект сокета-клиента
private static Socket Handler; // Создаем объект вспомогательного сокета
// ----------------------------------------------------------------------
// Деструктор
~SocketWorker()
{
// Вместо проверки сокетов на подключение, просто используем блок try-catch
try
{
// Сразу обрываем соединения
Server.Shutdown(SocketShutdown.Both);
// А потом закрываем сокет!
Server.Close();

Client.Shutdown(SocketShutdown.Both);
Client.Close();

Handler.Shutdown(SocketShutdown.Both);
Handler.Close();
}
catch { }
}
// ----------------------------------------------------------------------
// Создание сокета
public static bool IsConnected = false;
public static void CreateSocket(Object obj)
{
Object[] TempObject = (Object[])obj;
// IP-адрес сервера, для подключения
string HostName = (string)TempObject[0];
// Порт подключения
string Port = (string)TempObject[1];
bool ServerApp = (bool)TempObject[2];

// Разрешает DNS-имя узла или IP-адрес в экземпляр IPHostEntry.
ipHost = Dns.Resolve(HostName);
// Получаем из списка адресов первый (адресов может быть много)
ipAddr = ipHost.AddressList[0];
// Создаем конечную локальную точку подключения на каком-то порту
ipEndPoint = new IPEndPoint(ipAddr, int.Parse(Port));

if (!ServerApp)
{
try
{
// Создаем сокет на текущей машине
Client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
while (true)
{
// Пытаемся подключиться к удаленной точке
Client.Connect(ipEndPoint);
if (Client.Connected)
IsConnected = true;
break;
}
}
catch (SocketException error)
{
// 10061 - порт подключения занят/закрыт
if (error.ErrorCode == 10061)
{
MessageBox.Show("Порт подключения закрыт!");
Application.Exit();
}
}
}
else
{
try
{

// Создаем сокет сервера на текущей машине
Server = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
}
catch (SocketException error)
{
// 10061 - порт подключения занят/закрыт
if (error.ErrorCode == 10061)
{
MessageBox.Show("Порт подключения закрыт!");
Application.Exit();
}
}

// Ждем подключений
try
{
// Связываем удаленную точку с сокетом
Server.Bind(ipEndPoint);
// Не более 10 подключения на сокет
Server.Listen(10);

// Начинаем "прослушивать" подключения
while (true)
{
Handler = Server.Accept();
if (Handler.Connected)
IsConnected = true;
break;
}
}
catch
{
throw new Exception("Проблемы с подключением");
}
}
}
// ----------------------------------------------------------------------
// Получение данных от сервера
public static string GetDataFromServer()
{
string GetInformation = "";

// Создаем пустое «хранилище» байтов, куда будем получать информацию
byte[] GetBytes = new byte[1024];
int BytesRec = Client.Receive(GetBytes);

// Переводим из массива битов в строку
GetInformation += Encoding.Unicode.GetString(GetBytes, 0, BytesRec);

return GetInformation;
}
// ----------------------------------------------------------------------
// Получение информации от клиента
public static string GetDataFromClient()
{
string GetInformation = "";

byte[] GetBytes = new byte[1024];
int BytesRec = Handler.Receive(GetBytes);

GetInformation += Encoding.Unicode.GetString(GetBytes, 0, BytesRec);

return GetInformation;
}
// ----------------------------------------------------------------------
// Отправка информации на сервер
public static void SendDataToServer(string Data, string Name)
{
// Используем unicode, чтобы не было проблем с кодировкой, при приеме информации
byte[] SendMsg = Encoding.Unicode.GetBytes(Name + " : " + Data);
Client.Send(SendMsg);
}
// ----------------------------------------------------------------------
// Отправка информации на клиент
public static void SendDataToClient(string Data, string Name)
{
byte[] SendMsg = Encoding.Unicode.GetBytes(Name + " : " + Data);
Handler.Send(SendMsg);
}
// ----------------------------------------------------------------------
}
}




Вот и все, ничего тяжелого. Создаем и настраиваем удаленную локальную точку, сокет и пытаемся подключиться.
Интерфейс программы

Теперь нужно сделать интерфейс нашего чата. Он состоит из 2 окон:

Окно настроек подключения.

Сервер: ServerRadio

Клиент: ClientRadio

IP-адрес: IP-adress

Ник в чате: ChatNick

Чатиться =): StartChat

Поле ввода: ChatText

Отправить: Send

История чата: ChatHistory

«Ядро» программы

Еслы бы мы писали более сложную программу, тогда нужно было бы создать 2 класса, характеризирующих сервер и клиент, или же чат, где больше возможностей. Делать это нам не нужно, поэтому создайте 1 новый класс: AppCore.cs.using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestChat
{
class AppCore
{
// Тип приложения --------------------------------------------
public enum AppType { Server, Client }
private AppType ApplicationType;
public AppType ApplicationTypeProp
{
get { return ApplicationType; }
set { ApplicationType = value; }
}
// Ник в чате -----------------------------------------------
private string ChatNick = "BadUser";
public string ChatNickProp
{
get { return ChatNick; }
set { ChatNick = value; }
}
// ----------------------------------------------------------
}
}




Теперь сделаем один основной класс со статистическими ф-циями, в котором запишем основные функции. Назовите его Worker. Вот код:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace TestChat
{
class Worker
{
// Глобальный объект, характеризирующий персону в чате --------------------
public static AppCore GlobalAppObject;
// Настраивает объект выше
public static void CreateGameObject(bool IsServer, string ChatNick)
{
GlobalAppObject = new AppCore();
// Назначаем ник
GlobalAppObject.ChatNickProp = ChatNick;

// Тип приложения
if (IsServer)
{
GlobalAppObject.ApplicationTypeProp = AppCore.AppType.Server;
}
else
{
GlobalAppObject.ApplicationTypeProp = AppCore.AppType.Client;
}
}
// Ф-ция, работающая в новом потоке: получение новых сообщенй -------------
public static void GetMessages(Object obj)
{
// Получаем объект истории чата (лист бокс)
Object[] Temp = (Object[])obj;
System.Windows.Forms.ListBox ChatListBox =
(System.Windows.Forms.ListBox)Temp[0];

// В бесконечном цикле получаем сообщения
while (true)
{
// Ставим паузу, чтобы на время освобождать порт для отправки сообщений
Thread.Sleep(50);
if (GlobalAppObject.ApplicationTypeProp == AppCore.AppType.Server)
{
try
{
// Получаем сообщение от клиента
string Message = SocketWorker.GetDataFromClient();
// Добавляем в историю + текущее время
ChatListBox.Items.Add(DateTime.Now.ToShortTimeString() + " " + Message);
// Автопрокрутка списка
ChatListBox.TopIndex = ChatListBox.Items.Count - 1;
}
catch { }
}
else
{
try
{
string Message = SocketWorker.GetDataFromServer();
ChatListBox.Items.Add(DateTime.Now.ToShortTimeString() + " " + Message);
ChatListBox.TopIndex = ChatListBox.Items.Count - 1;
}
catch { }
}
}
}
// -----------------------------------------------------------------------
}
}



Окно настроек подключения

С его интерфейсом мы уже познайомились. Единственное, добавьте новый таймер на форму 300 миллисекунд, Enabled, UpdateForm. вот код формы:using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TestChat
{
public partial class WindowSettings : Form
{
public WindowSettings()
{
InitializeComponent();
// Назначаем потоку имя ф-ции
WaitingForConnecting = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(SocketWorker.CreateSocket));
}

// Указывает на тип приложения
private static bool AppCheckedTypeServer = true;
// Обработчик таймера
private void UpdateForm_Tick(object sender, EventArgs e)
{
// Если подключились - закрываем окно настроек
if (SocketWorker.IsConnected)
{
try
{
// Пытаемся завершить поток подключений
WaitingForConnecting.Abort();
}
catch { }
// Закрываем окно настроек
this.Close();
}
if (ServerRadio.Checked)
{
// Блокируем поле ввода IP-адреса
IPAdress.Enabled = false;
AppCheckedTypeServer = true;
}
else
{
AppCheckedTypeServer = false;
IPAdress.Enabled = true;
}
}

private void StartChat_Click(object sender, EventArgs e)
{
// Блокируем кнопку от повторного нажатия
StartChat.Enabled = false;

// Настраиваем основной игровой объект
Worker.CreateGameObject(AppCheckedTypeServer, ChatNick.Text);

// Запускаем поток подключения
if (AppCheckedTypeServer)
{
// 0.0.0.0 -для прослушки внешней сети, не используйте 127.0.0.1 или localhost!
WaitingForConnecting.Start(new Object[] { "0.0.0.0", "12123", AppCheckedTypeServer });
}
else
{
// 12123 –порт подключения
WaitingForConnecting.Start(new Object[] { IPAdress.Text.ToString(), "12123", AppCheckedTypeServer });
}
}
}
}





Основное окно чата

Создайте Windows Form, с именем MainChat. Сразу же добавьте в MainChat.Designer код:using System.Threading;
...
Thread WaitingForMessage;




Теперь, если хотите, установите Anchor для объектов на форме, чтобы можно было делать ресайзинг.

Вот код формы:using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TestChat
{
public partial class MainChat : Form
{
public MainChat()
{
InitializeComponent();
// Создаем новый поток, указываем на ф-цию получения сообщений в классе Worker
WaitingForMessage = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(Worker.GetMessages));
// Запускаем, в параметрах передаем листбокс (история чата)
WaitingForMessage.Start(new Object[] {ChatHistory});
}

// Обработчик отправки сообщения (кнопки)
private void Send_Click(object sender, EventArgs e)
{
if (Worker.GlobalAppObject.ApplicationTypeProp == AppCore.AppType.Server)
{
// Посылаем клиенту новое сообщение
SocketWorker.SendDataToClient(ChatText.Text, Worker.GlobalAppObject.ChatNickProp);
}
else
{
SocketWorker.SendDataToServer(ChatText.Text, Worker.GlobalAppObject.ChatNickProp);
}
// Добавляем в историю свое же сообщение + ник + время написания
ChatHistory.Items.Add(DateTime.Now.ToShortTimeString() + " " + Worker.GlobalAppObject.ChatNickProp + ": " + ChatText.Text.ToString());
// Автопрокрутка истории чата
ChatHistory.TopIndex = ChatHistory.Items.Count - 1;
// Убираем текст из поля ввода
ChatText.Text = "";
}
}
}






Теперь остается добавить последний штрих. Отредактируйте файл Program.cs static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new WindowSettings());
Application.Run(new MainChat());
}


Здесь мы сразу запускаем окно настроек, а потом уже само окно чата.

Вот что у нас получилось:

Еще немного подкрутить, и ася отдыхает ;)



Источник:
Категория: С# | Добавил: Sumrak (14.11.2010) | Автор: E W
Просмотров: 41066 | Комментарии: 0 | Теги: | Рейтинг: 4.2/27
Всего комментариев: 0

Log in

Block title

Copyright sumrak © 2025