Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.81/21: Рейтинг темы: голосов - 21, средняя оценка - 4.81
1 / 1 / 0
Регистрация: 26.04.2016
Сообщений: 16
Записей в блоге: 2
1

Клиент-серверное приложение на потоковом сокете TCP

21.06.2016, 14:01. Показов 4128. Ответов 1

Author24 — интернет-сервис помощи студентам
В следующем примере используем TCP, чтобы обеспечить упорядоченные, надежные двусторонние потоки байтов. Построим завершенное приложение, включающее клиент и сервер. Сначала демонстрируем, как сконструировать на потоковых сокетах TCP сервер, а затем клиентское приложение для тестирования нашего сервера.
Следующая программа создает сервер, получающий запросы на соединение от клиентов. Сервер построен синхронно, следовательно, выполнение потока блокируется, пока сервер не даст согласия на соединение с клиентом. Это приложение демонстрирует простой сервер, отвечающий клиенту. Клиент завершает соединение, отправляя серверу сообщение <TheEnd>.
Вот полный код программы SocketServer.cs:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// SocketServer.cs
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
 
namespace SocketServer
{
    class Program
    {
        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);
 
                // Начинаем слушать соединения
                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();
            }
        }
    }
}
Давайте рассмотрим структуру данной программы.
Первый шаг заключается в установлении для сокета локальной конечной точки. Прежде чем открывать сокет для ожидания соединений, нужно подготовить для него адрес локальной конечной точки. Уникальный адрес для обслуживания TCP/IP определяется комбинацией IP-адреса хоста с номером порта обслуживания, которая создает конечную точку для обслуживания.
Класс Dns предоставляет методы, возвращающие информацию о сетевых адресах, поддерживаемых устройством в локальной сети. Если у устройства локальной сети имеется более одного сетевого адреса, класс Dns возвращает информацию обо всех сетевых адресах, и приложение должно выбрать из массива подходящий адрес для обслуживания.
Создадим IPEndPoint для сервера, комбинируя первый IP-адрес хост-компьютера, полученный от метода Dns.Resolve(), с номером порта:
C#
1
2
3
IPHostEntry ipHost = Dns.GetHostEntry("localhost");
IPAddress ipAddr = ipHost.AddressList[0];
IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);
Здесь класс IPEndPoint представляет localhost на порте 11000. Далее новым экземпляром класса Socket создаем потоковый сокет. Установив локальную конечную точку для ожидания соединений, можно создать сокет:
C#
1
Socket sListener = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
Перечисление AddressFamily указывает схемы адресации, которые экземпляр класса Socket может использовать для разрешения адреса.

В параметре SocketType различаются сокеты TCP и UDP. В нем можно определить в том числе следующие значения:

Dgram
Поддерживает дейтаграммы. Значение Dgram требует указать Udp для типа протокола и InterNetwork в параметре семейства адресов.
Raw
Поддерживает доступ к базовому транспортному протоколу.
Stream
Поддерживает потоковые сокеты. Значение Stream требует указать Tcp для типа протокола.
Третий и последний параметр определяет тип протокола, требуемый для сокета. В параметре РrotocolType можно указать следующие наиболее важные значения - Tcp, Udp, Ip, Raw.

Следующим шагом должно быть назначение сокета с помощью метода Bind(). Когда сокет открывается конструктором, ему не назначается имя, а только резервируется дескриптор. Для назначения имени сокету сервера вызывается метод Bind(). Чтобы сокет клиента мог идентифицировать потоковый сокет TCP, серверная программа должна дать имя своему сокету:

C#
1
sListener.Bind(ipEndPoint);
Метод Bind() связывает сокет с локальной конечной точкой. Вызывать метод Bind() надо до любых попыток обращения к методам Listen() и Accept().
Теперь, создав сокет и связав с ним имя, можно слушать входящие сообщения, воспользовавшись методом Listen(). В состоянии прослушивания сокет будет ожидать входящие попытки соединения:

C#
1
sListener.Listen(10);
В параметре определяется задел (backlog), указывающий максимальное число соединений, ожидающих обработки в очереди. В приведенном коде значение параметра допускает накопление в очереди до десяти соединений.

В состоянии прослушивания надо быть готовым дать согласие на соединение с клиентом, для чего используется метод Accept(). С помощью этого метода получается соединение клиента и завершается установление связи имен клиента и сервера. Метод Accept() блокирует поток вызывающей программы до поступления соединения.

Метод Accept() извлекает из очереди ожидающих запросов первый запрос на соединение и создает для его обработки новый сокет. Хотя новый сокет создан, первоначальный сокет продолжает слушать и может использоваться с многопоточной обработкой для приема нескольких запросов на соединение от клиентов. Никакое серверное приложение не должно закрывать слушающий сокет. Он должен продолжать работать наряду с сокетами, созданными методом Accept для обработки входящих запросов клиентов.
C#
1
2
3
4
5
6
while (true)
{
            Console.WriteLine("Ожидаем соединение через порт {0}", ipEndPoint);
 
            // Программа приостанавливается, ожидая входящее соединение
            Socket handler = sListener.Accept();
Как только клиент и сервер установили между собой соединение, можно отправлять и получать сообщения, используя методы Send() и Receive() класса Socket.

Метод Send() записывает исходящие данные сокету, с которым установлено соединение. Метод Receive() считывает входящие данные в потоковый сокет. При использовании системы, основанной на TCP, перед выполнением методов Send() и Receive () между сокетами должно быть установлено соединение. Точный протокол между двумя взаимодействующими сущностями должен быть определен заблаговременно, чтобы клиентское и серверное приложения не блокировали друг друга, не зная, кто должен отправить свои данные первым.

Когда обмен данными между сервером и клиентом завершается, нужно закрыть соединение используя методы Shutdown() и Close():

C#
1
2
handler.Shutdown(SocketShutdown.Both);
handler.Close();
SocketShutdown — это перечисление, содержащее три значения для остановки: Both - останавливает отправку и получение данных сокетом, Receive - останавливает получение данных сокетом и Send - останавливает отправку данных сокетом.

Сокет закрывается при вызове метода Close(), который также устанавливает в свойстве Connected сокета значение false.

Клиент на TCP

Вот полный код для SocketClient.cs и его объяснение:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// SocketClient.cs
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
 
namespace SocketClient
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                SendMessageFromSocket(11000);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                Console.ReadLine();
            }
        }
 
        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();
        }
    }
}
Единственный новый метод - метод Connect(), используется для соединения с удаленным сервером. На рисунке ниже показаны клиент и сервер в действии:
1
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
21.06.2016, 14:01
Ответы с готовыми решениями:

Клиент-серверное приложение: как определить, что сервер/клиент не отвечает в течении определенного времени
Пишу клиент-серверное приложение. Использую TCPListener и TCPClient. Вопрос: как определить что...

Клиент-серверное приложение
Клиент-серверное приложение между процессов с помощью именованых каналов Здравствуйте. Тут...

Клиент-серверное приложение
Заинтересовался написанием клиент-серверных приложений, с чего начать? что написать? есть ли...

клиент-серверное приложение
собственно проблема заключается в том, что при запуске клиента, и нажатии на кнопку (отправление...

1
Эксперт .NET
12078 / 8387 / 1281
Регистрация: 21.01.2016
Сообщений: 31,595
22.06.2016, 08:48 2
namemkazaza, такие вещи в личном блоге делать нужно...
0
22.06.2016, 08:48
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
22.06.2016, 08:48
Помогаю со студенческими работами здесь

Закрывается клиент-серверное приложение
Здравствуйте, делаю что то типа клиен-сервера. Использую сокеты. При подключении с клиента на...

Асинхронное клиент серверное приложение
Всем доброго времени суток, а так же спасибо всем кто загляну и не прошёл мимо. Сразу...

Как написать клиент-серверное приложение
Как сделать так чтоб программа обменивалась данными с другой програмой через глобальную сеть....

Клиент-серверное приложение по Интернету с динамическим IP
Доброго времени суток! Имеются следующие клиент и сервер, которые я пытаюсь &quot;подружить&quot; через...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
2
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru