Регистрация | Вход 
Просмотр статьи
Работа с сокетами в .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());
        }

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

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

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

 

Закачать проект или исполняемый файл можете по ссылке: http://wincode.org.ua/downloads/TestChat/

Удачи.

Тысленко Максим (Ockonal)
17.11.2008


Автор: Alexander
Дата публикации: 22.11.2008
Число просмотров: 7415

Возврат


Copyright 2007-2009 by Alexander Ignatyev