932.10K
Категория: ПрограммированиеПрограммирование

Winsock

1.

Winsock
ОГУ
Кафедра ВТиЗИ
Галимов Р.Р.

2.

Базовые сведения о сокетах

3.

Базовые сведения о сокетах
• Сокеты определяют конечные точки
взаимодействия (обычно через сеть).
• Они поддерживают целый ряд протоколов, самыми
популярными из которых сегодня являются User
Datagram Protocol (UDP) и Transmission Control
Protocol (TCP).
• UDP-сокеты не требуют установления логических
соединений и обычно применяются для
широковещательной и многоадресной (multicast)
связи. В UDP нет средств надежной доставки
сообщений и контроля правильного порядка
пакетов, поэтому за обнаружение потери пакетов,
устранение таких проблем и упорядочение пакетов
отвечает приложение-получатель.

4.

Базовые сведения о сокетах
• TCP-сокеты ориентированы на логические
соединения, предоставляя надежный
коммуникационный путь двумя конечными
точками. Важное преимущество TCP в том, что
он гарантирует доставку сообщений и правильный
порядок пакетов.
• TCP-сокеты могут быть либо клиентскими, либо
серверными. Серверный сокет ожидает запросы
на установление соединений, а клиентский —
инициирует соединение.
• Как только соединение между сокетами
установлено, клиент и сервер могут передавать
и принимать данные или закрыть это соединение.

5.

6.

Базовые сведения о сокетах
static void Main(string[] args)
{ // Устанавливаем для сокета локальную конечную точку
IPHostEntry ipHost = Dns.GetHostEntry("localhost");
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);
// Создаем сокет Tcp/Ip
Socket sListener = new Socket(ipAddr.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
// Назначаем сокет локальной конечной точке и слушаем
входящие сокеты
try {
sListener.Bind(ipEndPoint);
sListener.Listen(10);
// Начинаем слушать соединения

7.

Базовые сведения о сокетах
while (true) {
Console.WriteLine("Ожидаем соединение через порт {0}", ipEndPoint);
// Программа приостанавливается, ожидая входящее соединение
Socket handler = sListener.Accept();
string data = null;
// Мы дождались клиента, пытающегося с нами соединиться
byte[] bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data += Encoding.UTF8.GetString(bytes, 0, bytesRec);
// Показываем данные на консоли
Console.Write("Полученный текст: " + data + "\n\n");
// Отправляем ответ клиенту\
string reply = "Спасибо за запрос в " + data.Length.ToString() + " символов"; byte[] msg =
Encoding.UTF8.GetBytes(reply);
handler.Send(msg);
if (data.IndexOf("<TheEnd>") > -1)
{ Console.WriteLine("Сервер завершил соединение с клиентом.");
break; }
handler.Shutdown(SocketShutdown.Both);
handler.Close(); } }
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
} finally { Console.ReadLine(); } } }

8.

Базовые сведения о сокетах

9.

Базовые сведения о сокетах
static void Main(string[] args)
{
try
{
SendMessageFromSocket(11000);
} catch (Exception ex) {
Console.WriteLine(ex.ToString()); } finally {
Console.ReadLine(); } }

10.

Базовые сведения о сокетах
static void SendMessageFromSocket(int port)
{ // Буфер для входящих данных
byte[] bytes = new byte[1024];
// Соединяемся с удаленным устройством
// Устанавливаем удаленную точку для сокета
IPHostEntry ipHost = Dns.GetHostEntry("localhost");
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, port);
Socket sender = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
// Соединяем сокет с удаленной точкой
sender.Connect(ipEndPoint);
Console.Write("Введите сообщение: ");
string message = Console.ReadLine();
Console.WriteLine("Сокет соединяется с {0} ", sender.RemoteEndPoint.ToString()); byte[] msg =
Encoding.UTF8.GetBytes(message); // Отправляем данные через сокет int bytesSent =
sender.Send(msg); // Получаем ответ от сервера
int bytesRec = sender.Receive(bytes);
Console.WriteLine("\nОтвет от сервера: {0}\n\n", Encoding.UTF8.GetString(bytes, 0, bytesRec));
// Используем рекурсию для неоднократного вызова SendMessageFromSocket()
if (message.IndexOf("<TheEnd>") == -1)
SendMessageFromSocket(port);
// Освобождаем сокет
sender.Shutdown(SocketShutdown.Both);
sender.Close(); }

11.

Базовые сведения о сокетах

12.

Базовые сведения о сокетах
• Чтобы настроить серверный TCP-сокет в управляемом
коде, прежде всего нужно создать экземпляр класса
Socket.
• Его конструктор принимает три параметра: AddressFamily,
SocketType и ProtocolType.
• Параметр AddressFamily определяет используемую
сокетом схему адресации. Чаще всего в качестве этого
параметра используются значения InterNetwork (для
адресов IPv4) и InterNetworkV6 (для адресов IPv6).
• Параметр SocketType определяет тип коммуникационной
связи, осуществляемой при помощи сокета; к двум
наиболее распространенным типам относятся Stream (для
сокетов, ориентированных на логические соединения)
и Dgram (если сокет не требует логических соединений).
• Параметр ProtocolType определяет применяемый сокетом
протокол и принимает такие значения, как Tcp, Udp, Idp,
Ggp и т. д.

13.

Базовые сведения о сокетах
• Socket s = new
Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);

14.

Базовые сведения о сокетах
• Как только сокет создан, его можно привязать
к адресу.
• Привязка клиентского сокета к адресу не обязательна,
но в случае серверных сокетов она необходима.
• Чтобы привязать сокет к адресу, вызовите метод Bind
объекта Socket.
• Этому методу нужен адрес и порт, которые будут
сопоставлены с сокетом, поэтому в качестве
параметра он принимает экземпляр класса,
производного от EndPoint.
• Как правило, это объект класса IPEndPoint (кроме него
в .NET Framework входит только один класс,
производный от EndPoint, — IrDAEndPoint, который
служит для взаимодействия посредством
инфракрасного порта).

15.

Базовые сведения о сокетах
• Объект IPEndPoint является логическим
представлением конечной точки при сетевой
коммуникации и включает данные об IPадресе и порте.
• Конструктор IPEndPoint принимает IP-адрес и
номер порта.
• Номер порта - это просто целочисленный тип,
определяющий используемый порт, а IP-адрес
может быть представлен .NET-классом
IPAddress или длинным целым числом (при
этом каждый из сегментов IP-адреса
представляется одним байтом целочисленного
типа).

16.

Базовые сведения о сокетах
• Класс IPAddress содержит несколько
предопределенных IP-адресов, для доступа
к которым служат статические свойства;
двумя самыми полезными адресами при
работе с TCP-сокетами являются
IPAddress.Loopback и IPAddress.Any.

17.

Базовые сведения о сокетах
using System.Net.NetworkInformation;
namespace testsocket1
{
class Program
{
static void Main(string[] args)
{
foreach (NetworkInterface nic in
NetworkInterface.GetAllNetworkInterfaces())
{
Console.WriteLine(nic.Name);
foreach (UnicastIPAddressInformation addrInfo in
nic.GetIPProperties().UnicastAddresses)
{
Console.WriteLine("\t" + addrInfo.Address);
}
}
}
}
}

18.

Базовые сведения о сокетах

19.

Установление соединений
• Вероятно, приложению потребуется адрес
сервера — для установления соединения или
для привязки сокета к этому адресу.
• Один способ получения адреса сервера
предоставляет класс System.Net.Dns.
• Он содержит ряд статических методов,
служащих для преобразования IP-адреса в имя
хоста и наоборот.
• Метод Dns.GetHostEntry возвращает объект
IPHostEntry, содержащий соответствующую
информацию о компьютере и позволяющий
узнать все IP-адреса компьютера, которые
известны DNS-серверу.

20.

Установление соединений
• После вызова метода Bind метод Socket.Listen
конфигурирует для сокета внутренние
очереди.
• Когда клиент пытается подключиться
к серверу, в очередь помещается запрос
соединения.
• Метод Listen принимает один аргумент —
максимальное число запросов соединений,
которые могут находиться в очереди.
• Метод Socket.Accept извлекает из очереди
первый запрос и возвращает новый объект
Socket, который можно использовать для
коммуникационного взаимодействия
с клиентом.

21.

Асинхронный ввод-вывод
• При асинхронном вводе-выводе для
обработки входящих данных и соединений
используются методы обратного вызова,
поэтому нам незачем ни генерировать
и сканировать списки, ни создавать новые
потоки для обработки ввода-вывода.
.

22.

Асинхронный ввод-вывод
• В .NET применяется довольно простая модель
асинхронных операций.
• Вы должны лишь вызвать нужный Begin-метод
(BeginAccept, BeginSend, BeginReceive и т. д.), передав
ему соответствующий делегат метода обратного
вызова, а в методе обратного вызова вызвать
аналогичный End-метод (EndAccept, EndSend,
EndReceive и т. п.) для получения результатов.
• Все асинхронные Begin-методы принимают объект
состояния контекста, который может представлять все,
что вы захотите.
• По завершении асинхронной операции этот объект
является частью IAsyncResult, который передается
методу обратного вызова..

23.

Асинхронный
ввод-вывод
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
namespace Sockets
{
class СAsynchronousIoServer
{
private Socket _serversocket;
private Form1 form1;
int port;
private void AddMess(string mess)
{
form1.Invoke(form1.myDelegate,mess);
}
public СAsynchronousIoServer(int _port,Form1 _form1)
{
port = _port;
form1 = _form1;
}

24.

Асинхронный ввод-вывод
void SetupServer()
{
IPHostEntry localMachineInfo =
Dns.GetHostEntry(Dns.GetHostName());
IPEndPoint myEndPoint = new
IPEndPoint(localMachineInfo.AddressList[3],
port);
_serversocket = new
Socket(myEndPoint.Address.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
_serversocket.Bind(myEndPoint);
serversocket.Listen((int)SocketOptionName.M
axConnections);
}

25.

Асинхронный ввод-вывод
private class ConnectionInfo
{
public Socket Socket;
public byte[] Buffer;
}
private List<ConnectionInfo>
_connections =
new
List<ConnectionInfo>();

26.

Асинхронный ввод-вывод
private class ConnectionInfo
{
public Socket Socket;
public byte[] Buffer;
}
private List<ConnectionInfo>
_connections =
new
List<ConnectionInfo>();

27.

Асинхронный ввод-вывод
private class ConnectionInfo
{
public Socket Socket;
public byte[] Buffer;
}
private List<ConnectionInfo>
_connections =
new
List<ConnectionInfo>();
public void Start()
{
SetupServer();
_serversocket.BeginAccept(new
AsyncCallback(AcceptCallback),
_serversocket);
}

28.

Асинхронный ввод-вывод
private void AcceptCallback(IAsyncResult result)
{
ConnectionInfo connection = new ConnectionInfo();
try
{
// Завершение операции Accept
Socket s = (Socket)result.AsyncState;
connection.Socket = s.EndAccept(result);
connection.Buffer = new byte[255];
lock (_connections) _connections.Add(connection);
// Начало операции Receive и новой операции Accept
connection.Socket.BeginReceive(connection.Buffer,
0, connection.Buffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveCallback),
connection);
_serversocket.BeginAccept(new AsyncCallback(
AcceptCallback), result.AsyncState);
AddMess("Соединение установлено");
}
catch (SocketException exc)
{
CloseConnection(connection);
AddMess("Socket exception: " + exc.SocketErrorCode);
}
catch (Exception exc)
{
CloseConnection(connection);
AddMess("Exception: " + exc);
}
}

29.

private void ReceiveCallback(IAsyncResult result)
{
ConnectionInfo connection = (ConnectionInfo)result.AsyncState;
try
{
int bytesRead = connection.Socket.EndReceive(result);
AddMess(String.Format("Получено {0} байт", bytesRead));
AddMess("Текст:" + Encoding.ASCII.GetString(connection.Buffer, 0, bytesRead));
if (0 != bytesRead)
{
lock (_connections)
{
foreach (ConnectionInfo conn in _connections)
{
if (connection != conn)
conn.Socket.Send(connection.Buffer, bytesRead,
SocketFlags.None);
}
}
connection.Socket.BeginReceive( connection.Buffer, 0,
connection.Buffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveCallback), connection);
}
else CloseConnection(connection);
}
catch (SocketException exc)
{
CloseConnection(connection);
AddMess("Socket exception: " + exc.SocketErrorCode);
}
catch (Exception exc)
{
CloseConnection(connection);
AddMess("Exception: " + exc);
}
}

30.

private void
CloseConnection(ConnectionInfo ci)
{
ci.Socket.Close();
lock (_connections)
_connections.Remove(ci);
}

31.

Делегаты
• Делегат представляет собой объект, который может ссылаться на
метод. Следовательно, когда создается делегат, то в итоге
получается объект, содержащий ссылку на метод. Более того, метод
можно вызывать по этой ссылке. Иными словами, делегат
позволяет вызывать метод, на который он ссылается.
• По сути, делегат — это безопасный в отношении типов объект,
указывающий на другой метод (или, возможно, список методов)
приложения, который может быть вызван позднее. В частности,
объект делегата поддерживает три важных фрагмента
информации:
• адрес метода, на котором он вызывается;
• аргументы (если есть) этого метода;
• возвращаемое значение (если есть) этого метода.
• Как только делегат создан и снабжен необходимой информацией,
он может динамически вызывать методы, на которые указывает, во
время выполнения. Каждый делегат в .NET Framework (включая
специальные делегаты) автоматически снабжается способностью
вызывать свои методы синхронно или асинхронно. Этот факт
значительно упрощает задачи программирования, поскольку
позволяет вызывать метод во вторичном потоке выполнения без
ручного создания и управления объектом Thread.

32.

Определение типа делегата в C#
• Тип делегата объявляется с помощью
ключевого слова delegate. Ниже приведена
общая форма объявления делегата:
delegate возвращаемый_тип имя
(список_параметров);

33.

namespace ConsoleApplication1 {
// Создадим делегат
delegate int IntOperation (int i, int j);
class Program { // Организуем ряд методов
static int Sum(int x, int y)
{ return x + y; }
static int Prz(int x, int y)
{ return x * y; }
static int Del(int x, int y)
{ return x / y; }
static void Main() { // Сконструируем делегат
IntOperation op1 = new IntOperation(Sum);
int result = op1(5, 10);
Console.WriteLine("Сумма: " + result);
// Изменим ссылку на метод
op1 = new IntOperation(Prz);
result = op1(5, 10);
Console.WriteLine("Произведение: " + result);
Console.ReadLine();
}
}

34.

Основная форма
public partial class Form1 : Form
{ public delegate void AddListItem(string smess);
public AddListItem myDelegate;
private void AddDebugText(string smess)
{
listBox1.Items.Add(smess);
}
public Form1()
{
InitializeComponent();
myDelegate = new AddListItem(AddDebugText);
}
private void button1_Click(object sender, EventArgs e)
{
СAsynchronousIoServer sercver = new
СAsynchronousIoServer(Convert.ToInt32(txtBoxPort.Text),this);
sercver.Start();
}
}

35.

Основная форма

36.

Основная форма

37.

Основная форма
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
public Form1 frm;
}

38.

Основная форма
public partial class Form1 : Form
{
Socket client=null;
public delegate void AddItem(string str);
public AddItem addRes;
public void AddResponse(string str)
{
listBox1.Items.Add(str);
}
public Form1()
{
InitializeComponent();
addRes = new AddItem(AddResponse);
}

39.

private void button1_Click(object sender, EventArgs e)
{
IPAddress ipAddress = IPAddress.Parse(textBox1.Text);
IPEndPoint remoteEP = new IPEndPoint(ipAddress,
Convert.ToInt32(textBox2.Text));
client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
try
{
client.Connect(remoteEP);
}
catch(Exception ex)
{
MessageBox.Show("Не удалось подключиться к
серверу\n"+ex.Message);
client = null;
}

40.

try
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
state.frm = this;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception ex)
{
MessageBox.Show("Ошибка определения обработчика
получения данных\n" + ex.Message);
}
}

41.

private void button2_Click(object sender,
EventArgs e)
{
if (client == null)
return;
byte[] byteData =
Encoding.Unicode.GetBytes(textBox3.Text);
client.Send(byteData);
}

42.

private static void ReceiveCallback(IAsyncResult ar)
{
try
{
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
string response = Encoding.Unicode.GetString(state.buffer, 0,
bytesRead);
state.frm.Invoke(state.frm.addRes, response);
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
}
catch (Exception e)
{
MessageBox.Show("Error\n" + e);
}
}

43.

Литература
1. Дэрин Кили. Winsock.
https://msdn.microsoft.com/ruru/library/dd335942.aspx
English     Русский Правила