Клиент серверное приложение с windows forms

Create a simple chat TCP/IP Client & Server using SimpleTCP library in C#

Create a simple chat TCP/IP Client & Server using SimpleTCP library in C#

Step 1Click New Project, then select Visual C# on the left, then Windows and then select Windows Forms Application. Name your project «TCPIPDemo» and then click OK. Similar with «Client» project

chat tcp ip in c#Step 2: Right click on your project select Manage NuGet Packages -> Search simpletcp -> Install

install simple tcp

Step 3: Design your form as below

Client

client chat in c#

Server

server chat in c#

You can change the host and port to connect over the network, connect through the network you should open the firewall to allow connection to your port

Add code to handle client form

namespace Client
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        SimpleTcpClient client;

        private void btnConnect_Click(object sender, EventArgs e)
        {
            btnConnect.Enabled = false;
            //Connect to server
            client.Connect(txtHost.Text, Convert.ToInt32(txtPort.Text));
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            client = new SimpleTcpClient();
            client.StringEncoder = Encoding.UTF8;
            client.DataReceived += Client_DataReceived;
        }

        private void Client_DataReceived(object sender, SimpleTCP.Message e)
        {
            //Update message to txtStatus
            txtStatus.Invoke((MethodInvoker)delegate ()
            {
                txtStatus.Text += e.MessageString;                
            });
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            client.WriteLineAndGetReply(txtMessage.Text, TimeSpan.FromSeconds(3));
        }
    }
}

Add code to handle server form

namespace TCPIPDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        SimpleTcpServer server;
        private void Form1_Load(object sender, EventArgs e)
        {
            server = new SimpleTcpServer();
            server.Delimiter = 0x13;//enter
            server.StringEncoder = Encoding.UTF8;
            server.DataReceived += Server_DataReceived;
        }

        private void Server_DataReceived(object sender, SimpleTCP.Message e)
        {
            //Update mesage to txtStatus
            txtStatus.Invoke((MethodInvoker)delegate ()
            {
                txtStatus.Text += e.MessageString;
                e.ReplyLine(string.Format("You said: {0}", e.MessageString));
            });
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            //Start server host
            txtStatus.Text += "Server starting...";
            System.Net.IPAddress ip = System.Net.IPAddress.Parse(txtHost.Text);
            server.Start(ip, Convert.ToInt32(txtPort.Text));
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            if (server.IsStarted)
                server.Stop();
        }
    }
}

VIDEO TUTORIALS

Related Posts

  • Windows Forms: Form Load and Button click Event in C#
  • Windows Forms: How to load User control dynamically in C#
  • Windows Forms: Text to speech in C#
  • Windows Forms: How to insert Math Equation to RichTextBox in C#
  • Windows Forms: Multiple pages on the Form using Panel control in C#
  • Windows Forms: Add Combobox to DataGridView in C#
  • Windows Forms: Youtube Search with Paging in C#
  • Windows Forms: Printing Text example in a Windows Form using C#

Пример создания распределенного приложения, демонстрирующего удаленное взаимодействие между компьютерами в сети. Пространство имен System.Runtime.Remoting. Протокол Http

В данной теме описывается один из способов разработки простейшего распределенного приложения, которое использует удаленное взаимодействие между компьютерами в сети. По данному примеру можно научиться разрабатывать приложения, которые взаимодействуют между собой в локальной сети. Тема предусматривает наличие базовых навыков работы в Microsoft Visual Studio 2010.


Содержание

  • Условие задачи
  • Соображения
  • Выполнение
    • 1. Запустить Microsoft Visual Studio 2010
    • 2. Разработка общей сборки с метаданными
      • 2.1. Что такое общая сборка?
      • 2.2. Создание модуля общей сборки
      • 2.3. Текст модуля общей сборки
      • 2.4. Объяснение к методу решения квадратного уравнения
      • 2.5. Компиляция. Создание .dll — файла общей сборки
      • 2.6. Закрыть решение (проект)
    • 3. Создание программного кода, который будет размещаться на компьютере-сервере
      • 3.1. Создание приложения типа Console Application
      • 3.2. Добавление ссылок на сборки
        • 3.2.1. Добавление ссылки на сборку System.Runtime.Remoting
        • 3.2.2. Добавление ссылки на сборку ClassLibrary1
        • 3.2.3. Подключение пространств имен сборок ClassLibrary1 и System.Runtime.Remoting в программный код файла Program.cs
      • 3.3. Написание кода серверной части
        • 3.3.1. Действия, которые нужно выполнить на сервере
        • 3.3.2. Текст кода серверной части
      • 3.4. Компиляция. Файлы серверной части
    • 4. Разработка приложения клиентской части
      • 4.1. Создание приложения клиентской части по шаблону Windows Forms
      • 4.2. Проектирование формы
        • 4.2.1. Размещение элементов управления LabelButtonTextBox на форме
        • 4.2.2. Настройка свойств фомы и элементов управления
      • 4.3. Текст модуля формы Form1.cs
      • 4.4. Алгоритм работы программы-клиента
      • 4.5. Написание программного кода клиентской части
        • 4.5.1. Добавление ссылок на сборки
        • 4.5.2. Подключение пространств имен
        • 4.5.3. Написание кода метода Form1_Load()
        • 4.5.4. Программирование события клика на кнопке «Вычислить». Текст клиентской части
    • 5. Тестирование взаимодействия между клиентом и сервером на одном компьютере без использования локальной сети
    • 6. Копирование серверной части на другой компьютер
    • 7. Определение имени сервера в сети из компьютера-клиента
    • 8. Корректирование клиентского программного кода. Замена имени localhost
    • 9. Тестирование взаимодействия между клиентом и сервером на разных компьютерах, объединенных между собой в локальную сеть
  • Связанные темы

Поиск на других ресурсах:


Условие задачи

1. Разработать распределенное приложение, которое содержит следующие части:

  • серверная часть. Сервер должен содержать функции решения математических задач. В данном случае, в качестве примера сервер содержит метод, который решает квадратное уравнение. Серверная часть должна быть разработана как консольное приложение;
  • клиентская часть. Клиент содержит программный код, который получает результат из серверной части. Клиент вызывает функцию решения квадратного уравнения, которая реализована и выполняется на сервере. Клиентская часть должна быть разработана как приложение типа Windows Forms.

2. Взаимодействие между клиентом и сервером организовать на основе протокола Http.

3. Приложение реализовать в системе Microsoft Visual Studio 2010 с использованием языка программирования C# .NET.

4. Для организации взаимодействия между компьютерами использовать средства из пространства имен System.Runtime.Remoting.

  ⇑

Соображения

Для проверки правильности выполнения задачи, нужно наличие как минимум двух компьютеров, которые объединенные между собой в локальную сеть.
Разработка распределенного приложения осуществляется на одном компьютере в MS Visual Studio 2010. Тестирование взаимодействия клиентской и серверной частей осуществляются также на одном компьютере.
После завершения тестирования, серверный код размещается на компьютере-сервере. Клиентский код из компьютера-клиента делает запрос на компьютер-сервер, получает ответ и выводит этот ответ на экран. В нашем случае клиентский код вызывает серверную функцию решения квадратного уравнения.

 

Выполнение

1. Запустить Microsoft Visual Studio 2010

 

2. Разработка общей сборки с метаданными

2.1. Что такое общая сборка?

Для того, чтобы клиент и сервер взаимодействовали между собой, нужно наличие еще одной части программного кода – общей сборки. Общая сборка содержит метаданные типов, которые являются допустимыми для удаленного вызова. Метаданные типов – это информация о типах данных, которые используются при удаленном вызове. Эта информация нужна как клиенту, так и серверу.
В конечном счете, после создания, программный код общей сборки должен размещаться как на компьютере клиента, так и на компьютере сервера. В противном случае, связь между клиентом и сервером невозможно будет установить.

 

2.2. Создание модуля общей сборки

В нашем случае общая сборка не является исполняемым файлом. Это есть библиотека, которая сохраняется в формате *.dll.
В Microsoft Visual Studio 2010 для создания библиотеки используется шаблон Class Library. В данном примере, чтобы создать модуль общей сборки нужно выполнить команду

File -> New -> Project...

Откроется окно «New Project», в котором нужно выбрать Visual C# -> Class Library. В нашем случае папка для размещения библиотеки: «D:Programs». Имя библиотеки ClassLibrary1 (по умолчанию). По желанию, можно изменить папку и имя библиотеки.

Visual Studio C# Создание библиотеки шаблон Class Library

Рис. 1. Создание библиотеки по шаблону Class Library

После выбора OK, создается проект, который содержит один файл Class1.cs. Листинг файла следующий:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClassLibrary1
{
    public class Class1
    {
    }
}

По умолчанию создается класс с именем Class1 в пространстве имен ClassLibrary1. В нашем случае оставляем все как есть. По желанию можно изменить название класса и пространства имен.

 

2.3. Текст модуля общей сборки

В модуль Class1.cs нужно ввести необходимый программный код. Этот код будет выполняться на стороне сервера. А вызов этого кода будет осуществлять клиент.
В соответствии с условием задачи, сервер может решать математические задачи. Поэтому, в класс Class1 нужно вписать функцию решения квадратного уравнения. В нашем случае функция называется CalcEquation(). По желанию, сюда можно вписать другие функции, которые решают разные задачи.
Также класс Class1 наследует данные и методы класса MarshalByRefObject. Класс MarshalByRefObject обеспечивает доступ к объектам в пределах домена приложения в приложениях, которые поддерживают удаленный доступ. Это специально разработанный класс для поддержки распределенного доступа к приложениям.

В классе MarshalRefObject есть метод CreateObjRef(), который будет использован в нашем проекте. Метод CreateObjRef() содержит всю необходимую информацию для генерирования прокси. Прокси применяется чтобы соединиться с удаленным объектом.

Листинг модуля общей сборки следующий (файл Class1.cs):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClassLibrary1
{
    // класс Class1 наследует класс MarshalByRefObject
    public class Class1 : MarshalByRefObject
    {
        // функция вычисления квадратного уравнения
        public bool CalcEquation(double a, double b, double c, out double x1, out double x2)
        {
            double d; // дискриминант

            // Решение квадратного уравнения
            // вычисление дискриминанта
            d = b * b - 4 * a * c;

            if (d > 0)
            {
                x1 = (-b - Math.Sqrt(d)) / (2 * a);
                x2 = (-b + Math.Sqrt(d)) / (2 * a);
                return true;
            }
            else
            if (d == 0)
            {
                x1 = x2 = (-b) / (2 * a);
                return true;
            }
            else
            {
                x1 = x2 = 0;
                return false;
            }
        }
    }
}

 

2.4. Объяснение к методу решения квадратного уравнения

Метод решения квадратного уравнения CalcEquation() получает входными параметрами значения коэффициентов a, b, c. Если уравнение имеет решение, то метод возвращает true и значение корней уравнения в параметрах-переменных x1, x2. Если уравнение не имеет решения, то метод возвращает false и нулевые значения параметров-переменных x1, x2.

 

2.5. Компиляция. Создание .dll — файла общей сборки

На этом шаге нужно откомпилировать проект командой

Build -> Build Solution

В результате будет создан *.dll-файл общей сборки с именем ClassLibrary1.dll, которая размещается в папке

D:ProgramsClassLibrary1ClassLibrary1binDebug

Этот файл нужно будет разместить в папках с исполняемыми модулями на компьютере-клиенте и на на компьютере-сервере. Файл общей сборки готов.

MS Visual Studio Файл ClassLibrary1.dll

Рис. 2. Файл ClassLibrary1.dll

 

2.6. Закрыть решение (проект)

Общая сборка разработана. Чтобы закрыть решение (Solution) нужно выполнить команду

File->Close Solution

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

 

3. Создание программного кода, который будет размещаться на компьютере-сервере

После создания файла (модуля) общей сборки на одном и том же компьютере нужно разработать программный код, который будет размещаться на компьютере-сервере.
Чтобы сервер отвечал на запросы клиента, программный код серверной части должен быть запущен на выполнение в момент запроса.

3.1. Создание приложения типа Console Application

В соответствии с условием задачи, на серверной части нужно создать приложение типа Console Application. Это осуществляется стандартным способом с помощью команды

File -> New -> Project...

В результате откроется окно New Project (рисунок 3), в котором нужно:

  • выбрать шаблон Visual C# -> Console Application;
  • оставить имя проекта по умолчанию ConsoleApplication1. По желанию можно изменить это имя;
  • выбрать папку для проекта D:Programs (или другую папку);
  • установить опцию «Create directory for solution».

Visual Studio C# Окно создание консольное приложение

Рис. 3. Окно создания консольного приложения

В результате будет создано обычное консольное приложение, которое содержит файл Program.cs. В этом файле объявляется пространство имен ConsoleApplication1, в котором реализован класс Program.

Листинг файла следующий

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

 

3.2. Добавление ссылок на сборки

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

  • сборку System.Runtime.Remoting. Эта сборка содержит все необходимые средства для удаленного доступа;
  • общую сборку ClassLibrary1, которая была разработана в п.2. В этой сборке размещается метод решения квадратного уравнения.

 

3.2.1. Добавление ссылки на сборку System.Runtime.Remoting

Чтобы добавить ссылку на сборку System.Runtime.Remoting нужно выполнить следующую последовательность команд.

В окне Solution Explorer вызвать команду Add Reference… как показано на рисунке 4. Для этого нужно сделать клик правой кнопкой мыши на элементе References.

Microsoft Visual Studio C# Команда "Add Reference..."

Рис. 4. Команда Add Reference…

В результате откроется диалоговое окно Add Reference (рисунок 5), в котором нужно:

  • активировать вкладку .NET;
  • в перечне сборок выбрать сборку System.Runtime.Remoting;
  • подтвердить выбор кнопкой OK.

MS Visual Studio C# Подключение сборки System.Runtime.Remoting.dll

Рис. 5. Подключение сборки System.Runtime.Remoting.dll

После этого в Solution Explorer в проекте ConsoleApplication1 будет отображена сборка System.Runtime.Remoting (рисунок 6).

MS Visual Studio сборка System.Runtime.Remoting

Рис. 6. Подключенная к проекту сборка System.Runtime.Remoting

 

3.2.2. Добавление ссылки на сборку ClassLibrary1

Добавление ссылки на сборку ClassLibrary1 осуществляется такой же командой «Add Reference…», как подключение сборки System.Runtime.Remoting (рисунок 4).
В результате откроется окно Add Reference, в котором нужно активировать вкладку Browse как показано на рисунке 7.
В окне, с помощью нисходящего меню «Папка» нужно выбрать папку, в которой размещается ранее разработанная общая сборка (см. п. 2) с именем ClassLibrary1.

В нашем случае выбирается папка (см. п. 2.4):

D:ProgramsClassLibrary1ClassLibrary1binDebug

MS Visual Studio C# файл общей сборки ClassLibrary1.dll

Рис. 7. Выбор папки и файла общей сборки

После выбора OK, в окне Solution Explorer в перечне References ссылок на использованные сборки появится сборка ClassLibrary1.

Visual Studio C# Сборка ClassLibrary1 серверное приложение

Рис. 8. Сборка ClassLibrary1 в перечне сборок серверного приложения

 

3.2.3. Подключение пространств имен сборок ClassLibrary1 и System.Runtime.Remoting в программный код файла Program.cs

После подключения сборок к проекту, нужно подключить необходимые пространства имен в файле Program.cs. Добавляются 4 пространства имен:

using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using ClassLibrary1;

На данный момент текст файла Program.cs следующий

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

// добавление пространств имен
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

using ClassLibrary1; // библиотека общей сборки

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

 

3.3. Написание кода серверной части
3.3.1. Действия, которые нужно выполнить на сервере

На компьютере-сервере нужно выполнить три основных действия:

  1. Создать канал связи. Для этого хорошо подходит класс HttpChannel. Есть и другие классы. Создается объект канала типа HttpChannel;
  2. Зарегистрировать канал с помощью статического класса ChannelServices. В этом классе есть метод RegisterChannel(). Этот метод получает параметром канал (тип HttpChannel в нашем случае) и опцию задействования службы безопасности (bool);
  3. Зарегистрировать сервис как WKO-тип (Well Known Object, хорошо известный объект). Это осуществляется с помощью класса RemotingConfiguration. В этом классе есть статический метод RegisterWellKnownServiceType(), который осуществляет регистрацию сервиса.

 

3.3.2. Текст кода серверной части

Ниже приведен текст программного кода серверной части

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

// добавление пространств имен
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;

using ClassLibrary1; // библиотека общей сборки

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1. Создать канал между клиентом и сервером, создать объект типа HttpChannel
            HttpChannel ch = new HttpChannel(5000); // номер порта = 5000 - выбран наугад

            // 2. Зарегистрировать канал ch, в методе RegisterChannel() указывается уровень безопасности false
            ChannelServices.RegisterChannel(ch, false);

            // 3. Зарегистрировать сервис как WKO
            RemotingConfiguration.RegisterWellKnownServiceType(
                typeof(ClassLibrary1.Class1),
                "MathFunctions.soap",
                WellKnownObjectMode.Singleton);

            // 4. Вывести сообщения, что сервис в состоянии выполнения
            Console.WriteLine("MathFunctions service is ready...");

            // 5. Остановить, оставить сервис в состоянии выполнения, пока не будет нажата клавиша Enter
            Console.ReadLine();
        }
    }
}

Объясним некоторые фрагменты кода серверной части.

Сначала создается канал связи, который представлен объектом ch типа HttpChannel. Конструктор класса HttpChannel() получает параметром номер порта, через который происходит связь между компьютерами. Номер порта может быть числом от 0 до 65535. Рекомендуется задавать номер порта выше значения 1024.
На втором этапе происходит регистрация канала ch с помощью класса ChannelServices. Это осуществляет метод класса RegisterChannel(). Первый параметр метода – это канал ch, который нужно зарегистрировать. Второй параметр – флажок типа bool. Значение флажка true требует запуск канала в пределах IIS (Internet Information Server) с целью обеспечения безопасности. Поскольку, наше приложение запускается за пределами IIS, то значение флажка нужно установить в false.

На третьем этапе нужно зарегистрировать сервис как WKO-тип (Well Known Object). Это реализуется с помощью класса RemotingConfiguration. В этом классе есть метод RegisterWellKnownServiceType(). Этот метод получает три параметра:

  • тип данных, который есть типом нашего созданного класса Class1 в общей сборке ClassLibrary1. В нашем случае здесь нужно передать typeof(ClassLibrary1.Class1);
  • строка, которая идентифицирует наш сервис на компьютере-сервере. В нашем случае выбрано «MathFunctions.soap». По желанию можно выбрать другое название;
  • значением из перечисления WellKnownObjectMode.Singleton. Это значит, что серверный объект разделяется всеми клиентами сервера.

В конце вызывается метод Console.Readline(). Этот метод необходим, чтобы зафиксировать выполнение консольного приложения, поскольку серверная часть должна быть все время запущена. В это время клиенты смогут к ней обращаться. Завершение работы сервера – клавиша Enter.

 

3.4. Компиляция. Файлы серверной части

На этом шаге нужно откомпилировать текст командой

Build -> Build Solution

для получения исполняемого (*.exe) файла. В результате будет создан исполняемый файл с именем

D:ProgramsConsoleApplication1ConsoleApplication1binDebugConsoleApplication1.exe

На рисунке 9 отображено содержимое папки Debug с файлами проекта серверной части. Как видно из рисунка 9, автоматически подгружается файл общей сборки ClassLibrary1.dll.

папка Debug файл ConsoleApplication1.exe ClassLibrary1.dll

Рис. 9. Содержимое папки Debug с исполняемым файлом ConsoleApplication1.exe серверной части и файлом сборки ClassLibrary1.dll

Оба файла ConsoleApplication1.exe и ClassLibrary1.dll нужно будет разместить (скопировать) на компьютере-сервере.

После этого проект, который соответствует серверной части, завершен.

 

4. Разработка приложения клиентской части

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

4.1. Создание приложения клиентской части по шаблону Windows Forms

Проект по шаблону Windows Forms создается стандартным способом:

File->New Project...

В результате откроется окно New Project. В окне New Project нужно выбрать вкладку

Visual C# -> Windows Forms Application

как показано на рисунке 10.

Проект Windows Forms Application

Рис. 10. Проект типа Windows Forms Application, который будет размещаться на компьютере клиента

В проекте задаются:

  • имя проекта WindowsFormsApplication1;
  • папка проекта D:Programs.

После выбора OK будет создана новая форма проекта как показано на рисунке 11.

Visual Studio C# форма проект

Рис. 11. Новая форма проекта

 

4.2. Проектирование формы
4.2.1. Размещение элементов управления Label, Button, TextBox на форме

Нужно разместить на форме следующие элементы управления (рисунок 12):

  • четыре элемента управления типа Label. По умолчанию создаются объекты с именами label1, label2, label3, label4;
  • один элемент управления типа Button. По умолчанию создается объект с именем button1.

MS Visual Studio C# элемент управления форма

Рис. 12. Размещение элементов управления на форме

 

4.2.2. Настройка свойств фомы и элементов управления

Подробное описание того, как осуществляется настройка элементов управления на форме можно найти в теме:

  • Пример программирования события в C#. Разработка программы для определения площади поверхности шара

На этом шаге, с помощью окна Properties, нужно настроить следующие свойства формы и элементов управления:

  • в форме Form1, свойство Text = «Решение квадратного уравнения»;
  • в форме Form1, свойство StartPosition = CenterScreen;
  • в Label1, свойство Text = «a = «;
  • в Label2, свойство Text = «b = «;
  • в Label3, свойство Text = «c = «;
  • в Label4, свойство Text = «Результат: «.

После настройки элементов управления, окно формы будет иметь вид как показано на рисунке 13.

MS Visual Studio C# Окно программа-клиент

Рис. 13. Окно программы-клиента после настройки

После этого можно разрабатывать программный код клиента.

 

4.3. Текст модуля формы Form1.cs

Главной форме программы-клиента соответствует модуль (файл) с именем Form1.cs.
На данный момент листинг файла Form1.cs имеет следующий вид:

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 WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
    }
}

 

4.4. Алгоритм работы программы-клиента

На компьютере клиенте нужно выполнить следующие действия:

  • создать канал соединения с сервером. В нашем случае используется класс HttpChannel. В отличие от сервера, клиенту номер порта не нужен. Поэтому, при создании класса будет вызываться конструктор HttpChannel() без номера порта. Для сервера номер порта обязательно нужно задавать, чтобы клиент знал где его найти. А клиенту номер порта не нужен, так как для создания канала система сама выберет первый подходящий порт;
  • зарегистрировать канал в системе с помощью класса ChannelServices, в котором есть соответствующий метод RegisterChannel();
  • создать экземпляр (объект) типа Class1. Этот класс есть реализованный в общей сборке (см. п.2). В этом классе есть метод вычисления квадратного уравнения CalcEquation(). Для создания экземпляра используется класс Activator. В классе Activator есть статический метод GetObject(), который создает прокси между клиентом и сервером для загруженного объекта WKO. Метод GetObject() возвращает объект типа Object, через который можно будет вызвать его отдаленные методы (в нашем случае, метод CalcEquation());
  • вызвать метод CalcEquation() из созданного экземпляра. Получить результат решения квадратного уравнения и вывести его на форму. Это все будет осуществляться в обработчике события клика на кнопке «Вычислить».

 

4.5. Написание программного кода клиентской части
4.5.1. Добавление ссылок на сборки

Для того, чтобы использовать код общей сборки, на компьютере-клиенте нужно подключить:

  • программный код общей сборки ClassLibrary1.dll;
  • программный код сборки System.Runtime.Remoting.dll.

Это осуществляется командой «Add Reference…» так же, как было сделано для серверной части (см. п.п. 3.2.1, 3.2.2).
После подключения, вкладка References в окне Solution Explorer будет иметь вид, как показано на рисунке 14.

Visual Studio C# Вкладка References окно Solution Explorer сборка

Рис. 14. Вкладка References в окне Solution Explorer. Подключенные сборки ClassLibrary1 и System.Runtime.Remoting

 

4.5.2. Подключение пространств имен

Как и в случае с серверной частью, в файле Form1.cs нужно подключить пространства имен общей сборки и System.Runtime.Remoting с помощью директивы using.

...

// подключение пространств имен
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using ClassLibrary1; // общая сборка

...

 

4.5.3. Написание кода метода Form1_Load()

Создание канала между сервером и клиентом в клиентской части будет выполняться только один раз. После того, как канал создан, многократно может вызываться метод CalcEquation() из серверной части.
Учитывая вышесказанное, целесообразно запрограммировать событие Load формы Form1. Это событие вызывается в начале создания формы. В нашей программе этому событию соответствует обработчик Form1_Load(). В обработчике нужно разместить программный код создания канала, регистрации канала и создания отдаленного объекта.

Ниже приведен текст обработчика события Form1_Load():

private void Form1_Load(object sender, EventArgs e)
{
    // Выполнение действий на стороне клиента
    // 1. Создать канал между клиентом и сервером
    HttpChannel ch = new HttpChannel(); // номер порта не нужен

    // 2. Зарегистрировать канал
    ChannelServices.RegisterChannel(ch, false);

    // 3. Создать объект удаленного доступа к серверу
    remote = (Class1)Activator.GetObject(
                 typeof(Class1),
                  "http://localhost:5000/MathFunctions.soap"); // localhost - временное имя нашего компьютера
}

 

4.5.4. Программирование события клика на кнопке «Вычислить». Текст клиентской части

На этом шаге нужно запрограммировать обработку события клика на кнопке «Вычислить» (button1). Подробное описание того как программируется событие описывается в теме:

  • Пример программирования события в C#. Разработка программы для определения площади поверхности шара.

Текст программы-клиента с реализованным обработчиком события клика на кнопке «Вычислить» приведен ниже

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;

// подключение пространств имен
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Http;
using ClassLibrary1; // общая сборка

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        Class1 remote; // переменная, которая представляет удаленный доступ

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 4. Вызвать метод CalcEquation() из серверной части для вычисления квадратного уравнения
            double a, b, c;
            double x1, x2;
            bool f;
            a = Double.Parse(textBox1.Text);
            b = Double.Parse(textBox2.Text);
            c = Double.Parse(textBox3.Text);

            // вызов удаленного метода
            f = remote.CalcEquation(a, b, c, out x1, out x2);

             // вывод результата
             if (f)
                 label4.Text = "Результат: x1 = " + x1.ToString() + "; x2 = " + x2.ToString();
             else
                 label4.Text = "Уравнение не имеет корней.";
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Выполнение действий на стороне клиента
            // 1. Создать канал между клиентом и сервером
            HttpChannel ch = new HttpChannel(); // номер порта не нужен

             // 2. Зарегистрировать канал
             ChannelServices.RegisterChannel(ch, false);

             // 3. Создать объект удаленного доступа к серверу
             remote =  (Class1)Activator.GetObject(
              typeof(Class1),
               "http://localhost:5000/MathFunctions.soap"); // localhost - временное имя нашего компьютера
        }
    }
}

В вышеприведенном коде пока что задается общее имя нашего компьютера localhost. Если будет сеть, то это имя нужно будет заменить на компьютере-клиенте на сетевое имя компьютера сервера. Клиентский проект нужно будет снова перекомпилировать.

 

5. Тестирование взаимодействия между клиентом и сервером на одном компьютере без использования локальной сети

Тестирование взаимодействия между клиентом и сервером проводится на одном компьютере, который в системе носит имя localhost. После проведения тестирования это имя будет изменено на имя компьютера-сервера в сети.

Для проведения тестирования нужно:

  • запустить серверное приложение (рисунок 15) из папки сервера (см. п. 3);
  • запустить на выполнение клиентское приложение (рисунок 16) из папки клиента (см. п. 4);
  • ввести в клиентское приложение данные и провести вычисления.

Visual Studio C# серверная часть выполнение

Рис. 15. Выполнение серверной части

Приблизительный результат выполнения клиентского приложения приведен на рисунке 16.

MS Visual Studio C# Клиентская программа

Рис. 16. Клиентская программа

Важно: если закрыть программу-сервер и попробовать провести вычисления на клиенте, то возникнет исключительная ситуация. Система не сможет найти приложение-сервер. Значит, для проведения тестирования, сервер должен все время выполняться.
Клиентов можно запускать сколько угодно и производить запросы к серверу.

 

6. Копирование серверной части на другой компьютер

На этом шаге нужно скопировать файлы ConsoleApplication1.exe и ClassLibrary1.dll на компьютер-сервер. Это осуществляется с помощью, например, обычного файлового менеджера. Файлы копируются в заведомо подготовленную папку.

 

7. Определение имени сервера в сети из компьютера-клиента

Если компьютеры объединены в локальную сеть, то каждый из них имеет свое уникальное имя. Способ определения имени компьютера в сети зависит от конкретной реализации операционной системы. В общем случае, чтобы определить это имя, нужно вызвать утилиту Control Panel (Панель управления). В этой утилите можно выбрать элемент System (Система). Откроется окно, в котором можно прочитать имя компьютера.

 

8. Корректирование клиентского программного кода. Замена имени localhost

Если известно имя компьютера-сервера в локальной сети, тогда нужно в клиентской части сделать изменения. В исходном коде файла Form1.cs в методе Form_Load() вместо строки

"http://localhost:5000/MathFunctions.soap"

нужно ввести

"http://MyComp:5000/MathFunctions.soap"

где MyComp – имя компьютера-сервера в локальной сети. Скорей всего у вас будет другое имя.

Итак, текст обработчика события загрузки формы в нашем случае следующий:

...
private void Form1_Load(object sender, EventArgs e)
{
    // Выполнение действий на стороне клиента
    // 1. Создать канал между клиентом и сервером
    HttpChannel ch = new HttpChannel(); // номер порта не нужен

    // 2. Зарегистрировать канал
    ChannelServices.RegisterChannel(ch, false);

    // 3. Создать объект удаленного доступа к серверу
    remote = (Class1)Activator.GetObject(
               typeof(Class1),
                "http://MyComp:5000/MathFunctions.soap"); // MyComp - имя сервера в сети
}

...

После этого нужно перекомпилировать клиентский проект, чтобы сформировать новый исполняемый модуль. На рисунке 17 изображен файл WindowsFormsApplication1.exe клиентского модуля. В этой папке также есть общая сборка ClassLibrary1.dll.

Папка клиентский модуль WindowsFormsApplication1

Рис. 17. Папка с клиентским исполняемым модулем WindowsFormsApplication1

 

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

На этом этапе нужно протестовать работу между клиентом и сервером на разных компьютерах, которые объединенные между собой в локальную сеть.

Для этого, нужно:

  • с помощью файлового менеджера скопировать в некоторую папку на компьютер-сервер файлы ConsoleApplication1.exe и ClassLibrary1.dll;
  • запустить на сервере файл ConsoleApplication1.exe. Предварительно на сервере должна быть установлена библиотека .NET Framework 4.0;
  • скопировать в нужную папку файлы WindowsFormsApplication1.exe (клиентское приложение) и ClassLibrary1.dll;
  • запустить на клиентской части файл WindowsFormsApplication1.exe, ввести данные, проверить результат выполнения функции из сервера.

Важно: чтобы между клиентом и сервером было взаимодействие, компьютер-клиент должен иметь соответствующие права доступа к компьютеру-серверу. Например, на сервере может быть включен общий доступ к его ресурсам. Это все осуществляется соответствующими настройками на компьютере-сервере. В данной теме это не рассматривается.

 


Связанные темы

  • Пример создания Web-приложения в MS Visual Studio — C#

 

Пример 2. Создание простого клиент-серверного приложения

В этом примере мы разработаем простой сервер и простую клиентскую программу, ведущих между собой ‘неспешный’ диалог. Клиента построим по технике Windows Forms, а сервер — Windows Service. Сервер будет иметь набор готовых маркированных ответов, ждать маркированных запросов клиентов и отвечать им соответствующими сообщениями. Это настроит нас на создание еще более сложной системы — просмотру дистанционных рисунков из БД, которой мы займемся позже.

Создание клиента

Начнем с клиентской программы, которая может запускаться во многих экземплярах
(‘http://msdn.microsoft.com/ru-ru/library/system.net.sockets.tcplistener.accepttcpclient.aspx’)

  • Командой File/Add/New Project добавьте к решению NetworkStream новый проект с именем SimpleClient и назначьте его стартовым

  • Разместите на форме элементы ListBox, Button, TextBox и настройте их в соответствии с таблицей свойств

Таблица
19.7.

Элемент Свойство Значение
Form Text Client
Size 300; 300
ListBox (Name) listBox
Dock Top
Font Arial; 12pt
Items
  1. Привет!
  2. Лелик
  3. Как жизнь
  4. Оттопыремся сегодня?
  5. Ну тогда пока!
SelectionMode One
Size 292; 119
Button (Name) btnSubmit
AutoSize True
Font Arial; 10pt
Location 96; 127
Size 101; 29
Text Отправить
TextBox (Name) textBox
Dock Bottom
Location 0; 162
Multiline True
ScrollBars Vertical
Size 292; 105

Остальные нужные настройки элементов формы добавим программно.

  • Откройте файл Form1.cs и сделайте его таким (приводится полностью)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
    
// Дополнительные пространства имен
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
    
namespace SimpleClient
{
    public partial class Form1 : Form
    {
        int port = 12000;
        String hostName = "127.0.0.1";// local
        TcpClient client = null;// Ссылка на клиента
    
        public Form1()
        {
            InitializeComponent();
    
            // Выделить первый элемент списка
            listBox.SelectedIndex = 0;
            listBox.Focus();
    
            // Контекстное меню для очистки TextBox
            ContextMenuStrip contextMenu = new ContextMenuStrip();
            textBox.ContextMenuStrip = contextMenu;
            ToolStripMenuItem item = new ToolStripMenuItem("Очистить");
            contextMenu.Items.Add(item);
            item.MouseDown += new MouseEventHandler(item_MouseDown);
        }
    
        // Отослать запрос и получить ответ
        private void btnSubmit_Click(object sender, EventArgs e)
        {
            if (listBox.SelectedIndices.Count == 0)
            {
                MessageBox.Show("Выделите сообщение");
                return;
            }
    
            try
            {
                // Создаем клиента, соединенного с сервером
                client = new TcpClient(hostName, port);
                // Сами задаем размеры буферов обмена (Необязательно!)
                client.SendBufferSize = client.ReceiveBufferSize = 1024;
            }
            catch
            {
                MessageBox.Show("Сервер не готов!");
                return;
            }
    
            // Записываем запрос в протокол
            AddString("Клиент: " + listBox.SelectedItem.ToString());
    
            // Создаем потоки NetworkStream, соединенные с сервером
            NetworkStream streamIn = client.GetStream();
            NetworkStream streamOut = client.GetStream();
            StreamReader readerStream = new StreamReader(streamIn);
            StreamWriter writerStream = new StreamWriter(streamOut);
    
            // Отсылаем запрос серверу
            writerStream.WriteLine(listBox.SelectedItem.ToString());
            writerStream.Flush();
    
            // Читаем ответ
            String receiverData = readerStream.ReadLine();
    
            // Записываем ответ в протокол 
            AddString("Сервер: " + receiverData);
    
            // Закрываем соединение и потоки, порядок неважен
            client.Close();
            writerStream.Close();
            readerStream.Close();
        }
    
        // Добавление строки, когда TextBox включен в режиме Multiline
        private void AddString(String line)
        {
            StringBuilder sb = new StringBuilder(textBox.Text);
            StringWriter sw = new StringWriter(sb);
            sw.WriteLine(line);
            textBox.Text = sw.ToString();
        }
    
        // Очистка списка через контекстное меню
        void item_MouseDown(object sender, MouseEventArgs e)
        {
            textBox.Clear();
            listBox.Focus();
        }
    }
}

Получив соединение с сервером, мы создаем два потока NetworkStream и упаковываем их в оболочки, удобные для управления чтением/записью. Обмен с сервером отображаем в протоколе TextBox. Для очистки протокола динамически создали контекстное меню.

Класс TcpClient, который мы использовали в коде, является высокоуровневой (и упрощенной) оболочкой сокета (класса Socket ). Если потребуется более низкоуровневое управление сокетом (более детальное), то ссылка на него хранится в свойстве TcpClient.Client. Но поскольку это свойство защищенное ( protected ), то доступ к нему возможен только из производного от TcpClient класса.

  • Разберитесь с кодом клиента

Если сейчас запустить приложение SimpleClient, то оно будет работать, но при попытке что-то отослать на сервер, будет выдаваться сообщение, что сервер не готов. Сервера-то еще пока вообще нет, создадим его.

Создание сервера

Серверную программу сделаем резидентной по шаблону Windows Service, как мы это делали в предыдущем примере, хотя можно сделать и с интерфейсом, главное, чтобы он был запущен в единственном экземпляре на локальном компьютере. Если программа-сервер включена в глобальную сеть, то с используемым IP и портом она должна быть единственной в этой сети. Поэтому на использование сетевого IP для глобальной сети нужно получать разрешение.

  • Командой File/Add/New Project добавьте к решению NetworkStream новый проект с именем SimpleServer по шаблону Windows Service

  • В панели Solution Explorer вызовите на узле SimpleServer проекта контекстное меню и командой Add/New Item в появишемся окне мастера выберите элемент Installer Class

using System;
using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;
    
namespace SimpleServer
{
    [RunInstaller(true)]// Во время установки сборки следует вызвать установщик
    public partial class Installer1 : Installer
    {
        private ServiceInstaller serviceInstaller;
        private ServiceProcessInstaller serviceProcessInstaller;
    
        public Installer1()
        {
            // Создаем настройки для Службы
            serviceInstaller = new ServiceInstaller();
            serviceProcessInstaller = new ServiceProcessInstaller();
    
            // Имя Службы для машины и пользователя
            serviceInstaller.ServiceName = "SimpleServerServiceName";
            serviceInstaller.DisplayName = "SimpleServer";
            serviceInstaller.StartType = ServiceStartMode.Manual;// Запуск вручную
    
            // Как будет запускаться Служба
            this.serviceProcessInstaller.Account = ServiceAccount.LocalService;
            this.serviceProcessInstaller.Password = null;
            this.serviceProcessInstaller.Username = null;
    
            // Добавляем настройки в коллекцию текущего объекта
            this.Installers.AddRange(
                new Installer[]         
                {
                    serviceInstaller,
                    serviceProcessInstaller
                });
        }
    }
}
using System;
using System.Collections.Generic;
using System.Text;
    
// Дополнительные пространства имен
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.ServiceProcess;
using System.Collections;
    
namespace SimpleServer
{
    class Service1 : ServiceBase
    {
        TcpListener server = null;// Ссылка на сервер
        int port = 12000;
        String hostName = "127.0.0.1";// local
        IPAddress localAddr;
        String[] answers = {
                               "1. Ты кто?",
                               "2. Привет, Лелик!",
                               "3. Лучше всех!",
                               "4. Конечно, на полную катушку",
                               "5. До вечера!"
                           };
    
        // Конструктор
        public Service1()
        {
            localAddr = IPAddress.Parse(hostName);// Конвертируем в другой формат
    
            Thread thread = new Thread(ExecuteLoop);
            thread.IsBackground = true;
            thread.Start();
        }
    
        private void ExecuteLoop()
        {
            try
            {
                server = new TcpListener(localAddr, port);// Создаем сервер-слушатель
                server.Start();// Запускаем сервер
    
                String data;
    
                // Бесконечный цикл прослушивания клиентов
                while (true)
                {
                    if (!server.Pending())// Очередь запросов пуста
                        continue;
                    TcpClient client = server.AcceptTcpClient();// Текущий клиент
                    // Сами задаем размеры буферов обмена (Необязательно!)
                    // По умолчанию оба буфера установлены размером по 8192 байта
                    client.SendBufferSize = client.ReceiveBufferSize = 1024;
    
                    // Подключаем NetworkStream и погружаем для удобства в оболочки
                    NetworkStream streamIn = client.GetStream();
                    NetworkStream streamOut = client.GetStream();
                    StreamReader readerStream = new StreamReader(streamIn);
                    StreamWriter writerStream = new StreamWriter(streamOut);
    
                    // Читаем запрос
                    data = readerStream.ReadLine();
                    // Отправляем ответ
                    int index;
                    if (int.TryParse(data.Substring(0, data.IndexOf('.')), out index))
                        data = answers[index - 1];
                    else
                        data = data.ToUpper();
                    writerStream.WriteLine(data);
                    writerStream.Flush();
    
                    // Закрываем соединение и потоки, порядок неважен
                    client.Close();
                    readerStream.Close();
                    writerStream.Close();
                }
            }
            catch (SocketException)
            {
            }
            finally
            {
                // Останавливаем сервер
                server.Stop();
            }
        }
    }
}

Чтобы передать данные и получить ответ, клиентское приложение создает двунаправленный сокет (или два однонаправленных), в котором указывает адрес соединения с сокетом другого, серверного, приложения. Если соединение установлено (сервер работает), то клиентское приложение подключает к сокету сетевой поток NetworkStream и через него выполняет передачу и прием данных.

На другой стороне соединения сервер TcpListener в бесконечном цикле прослушивает очередь соединений с клиентами. Если какой-то клиент с ним соединился ( server.Pending()!=false ), то сервер извлекает этого клиента методом AcceptTcpClient() — создает сокет для приема/передачи с готовым обратным адресом, создает двунаправленный поток (или два однонаправленных), затем читает запрос и передает ответ.

  • Запустите проект SimpleClient — все работает и результат такой

Обратите внимание, что если код работы нашей серверной программы не упаковать в отдельную нить Thread (поток выполнения), то в окне служб эта программа операционной системой запускаться не будет (попробуйте!). Причина в том, что в коде метода ExecuteLoop() в сервере используется бесконечный цикл прослушивания очереди запросов клиентов. Если этот цикл оставить в основном потоке выполнения ( Thread ) приложения, то оно просто зациклится и не сможет само нормально завершиться.
Поэтому код с циклом мы помещаем в отдельный поток (трэд) и делаем его фоновым, чтобы он закрывался вместе с основным потоком приложения (трэдом сервера).

Важное замечание

Поток NetworkStream является двухсторонним фиксированной длины. Методом GetStream() он только устанавливает адресное соединение между сокетами клиента и сервера. Но реальная его длина определяется сообщением отправляющей стороны. Можно для приема/передачи использовать один поток, но тогда длина сообщения, отправляемого сервером, не должна превышать длину сообщения, принятого им от клиента (чуть глаза не отсидел!). Поэтому мы и используем на каждой стороне два потока для раздельной однонаправленной передачи между двумя узлами сетевого соединения.

Пример 3. Клиент-серверное приложение просмотра рисунков из БД

На предыдущем простом примере мы познакомились (чуть-чуть) с пронципами создания сетевых приложений. А теперь построим более сложный пример, когда клиент запрашивает рисунки, а сервер извлекает их из хранилища и посылает клиенту. В Упражнении 7 нами было разработано три разных хранилища рисунков и три программы просмотра. В данном примере воспользуемся БД Pictures.my2.mdb с готовыми рисунками и на ее основе создадим сетевое приложение (для тех, кто не делал Упражнение 7, БД прилагается в каталоге Source/Data ).

Построение клиента

Для клиента построим оконное приложение типа WPF с пользовательским интерфейсом, частично заимствованным из Примера 6 Упражнения 7.

  • Командой File/Add/New Project добавьте к решению NetworkStream новый проект с именем PicturesClientDB типа WPF и назначьте его стартовым

  • Заполните дескрипторную часть окна Window1 (файл Window1.xaml ) следующим кодом (приводится полностью)
<Window x:Class="PicturesClientDB.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Клиентское приложение просмотра рисунков из БД " 
    Height="300"
    Width="470"
    MinHeight="300"
    MinWidth="450" 
    xmlns:local="clr-namespace:PicturesClientDB"
    >
    <Window.Resources>
        <local:Pictures x:Key="promptImage" />
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="110" />
        </Grid.ColumnDefinitions>
        <Border Name="border"
                Background="{Binding Source={StaticResource promptImage},
                Path=Picture, Mode=OneWay}">
            <Viewbox Name="Prompt">
                <Border 
                    BorderBrush="Black" 
                    BorderThickness="5" 
                    Background="Red" Margin="15,0">
                    <TextBlock
                        TextAlignment="Center"
                        FontWeight="Bold"
                        Padding="5,5"
                        >
                    <TextBlock.Foreground>
                        <SolidColorBrush Color="Yellow" />
                    </TextBlock.Foreground>
                    Сервер не готов, ждите!
                    <LineBreak />
                    Мы пытаемся связаться,
                    <LineBreak />
                    извините за неудобства...
                    </TextBlock>
                </Border>
            </Viewbox>
        </Border>
        <ListBox Name="listBox" Grid.Column="1" Padding="5,0"
                 ScrollViewer.VerticalScrollBarVisibility="Visible"
                 SelectionChanged="listBox_SelectionChanged">
        </ListBox>
    </Grid>
</Window>

Для вывода заставки с текстом о неготовности сервера мы применили элемент Viewbox, в который поместили еще один элемент Border с текстовым содержимым. Такой ‘огород’ позволит увеличивать заставку пропорционально размеру окна. Однако введение элемента Viewbox начинает заметно притормаживать перерисовку интерфейса при перемещениях окна, потому что он пытается постоянно пересчитывать масштабы своих дочерних элементов. Имена мы присвоили только тем интерфейсным элементам, которыми собираемся управлять в процедурном коде.

  • Заполните файл Window1.xaml.cs следующим процедурным кодом (приводится полностью)
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
    
// Дополнительные пространства имен для Stream
using System.IO;
using IO = System.IO;               // Псевдоним для адресации Path
using System.Windows.Threading;     // Для DispatcherTimer
    
// Дополнительные пространства имен для Socket
//using System.Net;
using System.Net.Sockets;
using System.Collections;   // List<byte>
    
namespace PicturesClientDB
{
    public partial class Window1 : Window
    {
        int port = 12000;
        String hostName = "127.0.0.1";  // local
        TcpClient client = null;        // Ссылка на клиента
        String sendMessage = "!!!GetNames!!!";    // Запрос на список (позаковыристей)
        Char[] separator = { '#' };     // Для преобразования ответа в массив имен
        DispatcherTimer timer;          // Таймер  
    
        // Конструктор
        public Window1()
        {
            InitializeComponent();
    
            // Создаем и запускаем таймер
            timer = new DispatcherTimer();
            timer.Tick += new EventHandler(timer_Tick);
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Start();
        }
    
        // Инициирует обращение к серверу
        void timer_Tick(object sender, EventArgs e)
        {
            Execute(listBox);
        }
    
        private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Execute((ListBox)sender);
        }
    
        void Execute(ListBox lst)
        {
            // Заполняем список именами рисунков
            try
            {
                // Если сервер доступен, создаем клиента
                client = new TcpClient(hostName, port);
            }
            catch
            {
                // Сервер не готов, запускаем таймер и выходим
                if (Prompt.Visibility != Visibility.Visible)
                {
                    Prompt.Visibility = Visibility.Visible;
                    timer.Start();
                }
                return;
            }
    
            switch (sendMessage)
            {
                case "!!!GetNames!!!":
                    // Получаем и привязываем имена рисунков к списку
                    lst.ItemsSource = GetNames();
                    // Выделяем первый элемент списка, чтобы вызвать SelectionChanged
                    lst.SelectedIndex = 0;
                    lst.Focus();
                    sendMessage = "";
                    break;
                default:
                    // Скрываем сообщение и останавливаем таймер
                    if (Prompt.Visibility == Visibility.Visible)
                    {
                        Prompt.Visibility = Visibility.Hidden;
                        timer.Stop();
                    }
    
                    // Получаем рисунок и отображаем пользователю кистью
                    String name = lst.SelectedValue.ToString();
                    BitmapImage bi = new BitmapImage();
                    bi.BeginInit();
                    // Получаем от сервера рисунок и обертываем его в поток памяти
                    bi.StreamSource = new MemoryStream(GetPicture(name));
                    bi.EndInit();
                    Pictures.picture.ImageSource = bi;// Передаем рисунок фону Border
                    break;
            }
        }
    
        private String[] GetNames()
        {
            String[] names;
    
            // Создаем потоки сетевых соединений
            StreamReader readerStream = new StreamReader(client.GetStream());
            StreamWriter writerStream = new StreamWriter(client.GetStream());
            // Отсылаем запрос серверу
            writerStream.WriteLine(sendMessage);
            writerStream.Flush();
    
            // Читаем ответ
            String receiverData = readerStream.ReadLine();
            names = receiverData.Split(separator);// Преобразуем в строковый массив
    
            // Закрываем соединение и потоки, порядок неважен
            client.Close();
            writerStream.Close();
            readerStream.Close();
    
            return names;
        }
    
        Byte[] GetPicture(String name)
        {
            // Создаем потоки сетевых соединений
            NetworkStream readerStream = client.GetStream();
            StreamWriter writerStream = new StreamWriter(client.GetStream());
    
            // Отсылаем запрос серверу
            writerStream.WriteLine(name);
            writerStream.Flush();
    
            // Читаем ответ 
            // ReceiveBufferSize - размер буфера для входящих данных
            // SendBufferSize - размер буфера для исходящих данных
            List<byte> list = new List<byte>(client.ReceiveBufferSize);// С приращением capacity
            Byte[] bytes = new Byte[client.ReceiveBufferSize]; // Размер буфера сокета
            int count = 0;  // Порции входящих данных
            while ((count = readerStream.Read(bytes, 0, bytes.Length)) != 0)
                for (int i = 0; i < count; i++)
                    list.Add(bytes[i]);
    
            // Преобразуем в массив результата
            bytes = new Byte[list.Count];
            list.CopyTo(bytes);
    
            // Закрываем соединение и потоки, порядок неважен
            client.Close();
            writerStream.Close();
            readerStream.Close();
    
            return bytes;
        }
    }
    
    // Для привязки к ресурсу
    class Pictures
    {
        // Поле
        public static ImageBrush picture = new ImageBrush();
    
        static Pictures()
        {
            // Дежурный рисунок заставки
            picture.ImageSource = new BitmapImage(
                new Uri(@"flower2.jpg", UriKind.Relative));
            picture.Stretch = Stretch.Fill;
            picture.Opacity = 1.0D;
        }
    
        // Привязываемое в интерфейсе свойство
        public static ImageBrush Picture { get { return picture; } }
    }
}

Обратите внимание, что при отображении рисунков мы отказались от традиционного элемента Image, как это делали в предыдущем упражнении. А для разнообразия поступили совершенно нетрадиционно (по турецки). Теперь мы рисунки будем отображать кистью ImageBrush в фоне прямоугольника Border через привязанный к нему объект Pictures. Конечно, в жизни так извращаться вряд ли придется, но и такой вариант где-нибудь может пригодиться.

Заставка появится сразу же, как будет обнаружен факт отсутствия или остановки сервера. А после обнаружения сервера заставка исчезнет. Этот механизм немедленно сработает благодаря используемому нами системному таймеру. Однако, сервера пока еще совсем нет и следует его изготовить.

Построение сервера БД как службу
  • Командой File/Add/New Project добавьте к решению NetworkStream новый проект с именем PicturesServerDB типа Windows Service

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
    
// Дополнительные пространства имен для ADO.NET
using System.Data.OleDb;
using System.Data.Common;
    
// Дополнительные пространства имен
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Collections;
    
namespace PicturesServerDB
{
    public partial class Service1 : ServiceBase
    {
        int port = 12000;
        String hostName = "127.0.0.1";  // local
        IPAddress localAddr;
        TcpListener server = null;      // Ссылка на сервер
        String separator = "#";         // Разделитель имен в строке ответа
        String connectionString;        // Строка соединения с БД
    
        public Service1()
        {
            // Извлекаем в поле строку соединения с БД из файла App.config
            connectionString = System.Configuration.ConfigurationManager.
                ConnectionStrings["PicturesDB"].ConnectionString;
    
            // Конвертируем IP в другой формат
            localAddr = IPAddress.Parse(hostName);
    
            // Запускаем в новом потоке (ните)
            Thread thread = new Thread(ExecuteLoop);
            thread.IsBackground = true;
            thread.Start();
        }
    
        private void ExecuteLoop()
        {
            try
            {
                server = new TcpListener(localAddr, port);// Создаем сервер-слушатель
                server.Start();// Запускаем сервер
    
                // Бесконечный цикл прослушивания клиентов
                while (true)
                {
                    // Проверяем очередь соединений
                    if (!server.Pending())// Очередь запросов пуста
                        continue;
                    TcpClient client = server.AcceptTcpClient();// Текущий клиент
    
                    // Создаем потоки сетевых соединений
                    StreamReader readerStream = new StreamReader(client.GetStream());
                    NetworkStream streamOut = client.GetStream();
                    StreamWriter writerStream = new StreamWriter(streamOut);
    
                    // Читаем команду клиента 
                    String receiverData = readerStream.ReadLine();
    
                    // Распознаем и исполняем
                    switch (receiverData)
                    {
                        case "!!!GetNames!!!":// Посылаем имена, разделенные сепаратором
                            String names = GetNames();
                            writerStream.WriteLine(names);  // Используем через оболочку
                            writerStream.Flush();
                            break;
                        default:// Посылаем рисунок
                            Byte[] bytes = GetPicture(receiverData);
                            streamOut.Write(bytes, 0, bytes.Length);// Используем напрямую
                            streamOut.Flush();
                            break;
                    }
    
                    // Закрываем соединение и потоки, порядок неважен
                    client.Close();
                    readerStream.Close();
                    writerStream.Close();
                }
            }
            finally
            {
                // Останавливаем сервер
                server.Stop();
            }
        }
    
        // Извлечение из БД имен рисунков и упаковка их в одну строку для пересылки клиенту
        string GetNames()
        {
            // Создаем и настраиваем инфраструктуру ADO.NET
            OleDbConnection conn = new OleDbConnection(connectionString);
            OleDbCommand cmd = new OleDbCommand("SELECT FileName FROM MyTable");
            cmd.Connection = conn;
            conn.Open();
    
            // Извлекаем имена рисунков
            OleDbDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
    
            // Формируем строку исходящих данных
            StringBuilder sb = new StringBuilder();
            foreach (DbDataRecord record in reader)// Равносильно чтению reader.Read()
                sb.Append(((string)record["FileName"]).Trim() + separator);
            // Соединение здесь закроет сам объект DataReader после прочтения всех данных
            // в соответствии с соглашением при его создании CommandBehavior.CloseConnection
    
            // Удаляем лишний последний символ сепаратора
            sb.Replace(separator, String.Empty, sb.ToString().
                LastIndexOf(separator), separator.Length);
    
            return sb.ToString();
        }
    
        // Извлечение из БД самого рисунка для отправки клиенту
        byte[] GetPicture(String name)
        {
            // Создаем и настраиваем инфраструктуру ADO.NET
            OleDbConnection conn = new OleDbConnection();
            conn.ConnectionString = connectionString;
    
            // Создаем и настраиваем объект команды, параметризованной по имени рисунка
            OleDbCommand cmd = new OleDbCommand();
            cmd.Connection = conn;
            cmd.CommandType = CommandType.Text; // Необязательно! Установлено по умолчанию
            cmd.CommandText = "SELECT Picture FROM MyTable WHERE FileName=?";
            cmd.Parameters.Add(new OleDbParameter());
            cmd.Parameters[0].Value = name;// Имя рисунка
            OleDbDataAdapter adapter = new OleDbDataAdapter(cmd);
    
            // Извлекаем рисунок из БД 
            DataTable table = new DataTable();
            adapter.Fill(table);
            byte[] bytes = (byte[])table.Rows[0]["Picture"]; // Подключаемся к рисунку
    
            return bytes;
        }
    }
}
  • В панели Solution Explorer контекстной командой Add/New Item для корня проекта добавьте в него конфигурационный файл App.config

  • Заполните файл App.config следующим кодом (приводится полностью)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="PicturesDB"
        connectionString="Provider=Microsoft.Jet.OLEDB.4.0;
        Jet OLEDB:Engine Type=5;
        Data Source=|DataDirectory|DataPictures.my2.mdb"
        providerName="System.Data.OleDb" />
  </connectionStrings>
</configuration>
  • В панели Solution Explorer контекстной командой Add/References для узла References добавьте к проекту ссылку на библиотеку System.Configuration.dll

Теперь создадим для сервиса инсталляционный файл.

using System;
using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;
    
namespace SimpleServer
{
    [RunInstaller(true)]// Во время установки сборки следует вызвать установщик
    public partial class Installer1 : Installer
    {
        private ServiceInstaller serviceInstaller;
        private ServiceProcessInstaller serviceProcessInstaller;
    
        public Installer1()
        {
            // Создаем настройки для Службы
            serviceInstaller = new ServiceInstaller();
            serviceProcessInstaller = new ServiceProcessInstaller();
    
            // Имя Службы для машины и пользователя
            serviceInstaller.ServiceName = "PicturesServerDB";
            serviceInstaller.DisplayName = "PicturesServerDB";
            serviceInstaller.StartType = ServiceStartMode.Manual;// Запуск вручную
    
            // Как будет запускаться Служба
            this.serviceProcessInstaller.Account = ServiceAccount.LocalService;
            this.serviceProcessInstaller.Password = null;
            this.serviceProcessInstaller.Username = null;
    
            // Добавляем настройки в коллекцию текущего объекта
            this.Installers.AddRange(
                new Installer[]         
                {
                    serviceInstaller,
                    serviceProcessInstaller
                });
        }
    }
}
  • Откомпилируйте проект командой Build/Build Solution

Теперь нужно зарегистрировать созданный сервис (для нас — сервер) в операционной системе.

Вот одна из картинок, когда работа клиента (или всех клиентов) была временно прекращена остановкой сервера

Чтобы созданный сервис не загружал компьютер, его можно деинсталлировать той же утилитой InstallUtil.exe, только с опцией /u.

В последнем примере мы немного перегорячились и сразу стали делать сервер как службу Windows, чтобы не было видно пользовательского интерфейса (даже консольного окна). Это совершенно необязательно, сервер можно сделать и как обычное приложение с интерфейсом пользователя и запускать его по мере необходимости.
Главное, что нужно помнить и что послужило причиной создания нами сервера как службы, — на одном IP -адресе не может сидеть сразу несколько запущенных серверов. Иначе бы получилась неопределенность, с каким именно сервером должны общаться клиенты. А службы как раз и запускаются в одном экземпляре.

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

138 / 7 / 1

Регистрация: 31.03.2015

Сообщений: 395

1

11.06.2016, 06:35. Показов 7193. Ответов 24


Не очень знаком с клиент-серверной схемой работы — но мне надо чтобы по такому принципу работала форма winforms, через которую надо вводить данные и наполнять бд, с которой соответственно получать данные, и выводить их в этой (или другой форме). Вот меня интересует в чем конкретно эта схема работы заключается. Будет ли бд на сервере, а клиент — это приложение формы? Просмотрел много примеров по работе сокетов преимущественно встречал передачу неких строк (сообщений), запись в файл — а как быть здесь: надо ведь заполнять/изменять таблицы бд. Надо какой то общий метод который будет отправлять данные на сервер/бд? Вообще возможна ли такая схема при использование sql express server /management studio и visual c# express? То есть если я сделаю вариант без использование клиент-сервера насколько сложно будет переработать под эту схему?



0



Эксперт .NETАвтор FAQ

10365 / 5096 / 1824

Регистрация: 11.01.2015

Сообщений: 6,226

Записей в блоге: 34

11.06.2016, 11:17

2

arts1, Не нужно вам никаких сокетов, используйте ADO.NET, оно умеет конектится к удаленному серверу.
Смотрите классы из ADO: SqlConnection, SqlCommand etc.
А вообще то, что вы делаете называется двухзвенная архитектура. Гуглите, материала — море.



0



138 / 7 / 1

Регистрация: 31.03.2015

Сообщений: 395

12.06.2016, 01:44

 [ТС]

3

Ну мне надо клиент- серверное приложение. Его что невозможно в этом случае реализовать? Например одна кнопка забирает данные с двух-трех текстовых полей, с групы радио-батонов или чек-боксов и заносит в таблицу. Другая делает это для другой таблицы. Еще две кнопки делают агрегацию/отчет тоже на основание отмеченых полей таблицы и введеных даных (даты). Еще остается вопрос возможно ли в экспрес версиях реализовать этот проэкт ибо читал что там возникает проблема сети, которая как бы локальная по умолчанию в экспресе или не существует вообще.

Добавлено через 10 часов 31 минуту
Ну может в моем случае форма-бд и играет роль схемы и клиент-сервер — ибо как увидел клиент-сервер это взаимодействие двух форм чего здесь нету. Но как понял с пересмотреного кода — передать через сеанс возможно лиш одно значение тестового поля? А как быть со значениями типа чекбокс, переключатели?… Но после попытки подключить к вижуал 2005 экспрес бд sql sеrvеr экспрес 2005 — This server version is not supported you must have microsoft sql server 2005 beta 2 or late — такая ошибка возникает. А как найти эту версию и решит ли она эту проблему? Тем более что в экспресе кажется невозможно работать со среды вижуал студио с бд ескьюэл сервер напрямую …



0



138 / 7 / 1

Регистрация: 31.03.2015

Сообщений: 395

13.06.2016, 07:59

 [ТС]

4

У меня такой вопрос по существу работы форм: если у меня есть група значений переключателей и чекбоксов — как мне их конвертировать в значение (типы) чтобы передать их в бд? В первом случае нашел пример что использовать еnums, то есть идет некая конвертация к int, а с чекбоксами — вводить типа boolean или int?



0



Эксперт .NET

5459 / 4232 / 1208

Регистрация: 12.10.2013

Сообщений: 12,223

Записей в блоге: 2

13.06.2016, 08:28

5

Цитата
Сообщение от arts1
Посмотреть сообщение

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

Если вопрос стоит именно так — не просто удаленно взаимодействовать с БД на сервере, а через свою логику — то тогда используйте трехзвенную архитектуру. БД на сервере, блок серверного ПО (логика доступа к данным) и клиентское приложение.
Могу посоветовать использовать WCF, это самая новая разработка MS в распределенных приложениях.



0



138 / 7 / 1

Регистрация: 31.03.2015

Сообщений: 395

13.06.2016, 10:49

 [ТС]

6

Нет мне надо в винформс, ну и хотябы в связке форма-бд



0



Эксперт .NET

5459 / 4232 / 1208

Регистрация: 12.10.2013

Сообщений: 12,223

Записей в блоге: 2

13.06.2016, 10:54

7

arts1, это уже зависит от вас.
Я бы, наверное, группировал так:
1. Расходы на продукты питания (можно разбить на более мелкие части)
2. Расходы на коммунальные платежи.
3. Бытовые расходы (покупка одежды, вещей и пр.).
4. Расходы на содержание автомобиля (если имеется), бензин, ТО и т.д.
5. Прочие расходы.



0



Эксперт .NETАвтор FAQ

10365 / 5096 / 1824

Регистрация: 11.01.2015

Сообщений: 6,226

Записей в блоге: 34

13.06.2016, 11:22

8

Цитата
Сообщение от arts1
Посмотреть сообщение

У меня такой вопрос по существу работы форм: если у меня есть група значений переключателей и чекбоксов — как мне их конвертировать в значение (типы) чтобы передать их в бд? В первом случае нашел пример что использовать еnums, то есть идет некая конвертация к int, а с чекбоксами — вводить типа boolean или int?

Сначала спроектируйте базу данных. Составьте то, что называется инфологическая модель — таблицы, поля, связи. И после этого, вам уже станет понятно к каким типам приводить ваши чекбоксы.
Что касается преобразования ваших типов в типы БД, то SqlCommand.Parameters сам умеет это делать, смотрите пример здесь.

Цитата
Сообщение от arts1
Посмотреть сообщение

Тем более что в экспресе кажется невозможно работать со среды вижуал студио с бд ескьюэл сервер напрямую …

Создать базу данных вы можете в SQL Server Menagement Studio (которое входит в состав MS SQL Server).



0



138 / 7 / 1

Регистрация: 31.03.2015

Сообщений: 395

13.06.2016, 21:40

 [ТС]

9

Ну если разбивать на более мелкие части то там надо уже какую то двухуровневую иерархию — или вы имеете ввиду просто все разделить на максимально подробные категории? Таблицу я уже составил (вроде 2 достаточно) на бумаге, каких то общих значений кроме общий доход=< общий расход не надо. Хотя если надо еще обязательно планированые расходы — то это как бы лиш дополнительное поле. Менеджмент студио использую — но уже писал что выскакивает ошибка о необходимости бета версии. Здесь еще вопрос возник — пытаюсь определить initializecomponent() в класе Form1 но что при компиляции вызывает ошибку что он уже определен (-яется). Касательно конвертации может .ToInt32(checked) для флажка подойдет? Потом еще момент насколько важен здесь момент правильности (или ввода вообще) ввода даных, нужно или определение даты использовать встроеные класы они ведь на английском или вручную вводить.

Добавлено через 6 часов 18 минут
Я вот не заметил сначала. В моей ошибки о подключение бд ведь идет речь о необходимости не экспрес, а полной microsoft sql server 2005 версии beta 2 — думаю это детали. Где найти эту версию (yukon называется), и сколько места на диске она занимает? Вот теперь понял какое серьезное ограничение что вижуал студио экспрес не может работать с sql server express.

Добавлено через 1 час 7 минут
Да действительно может (если это возможно) обойтись лиш sql server management studio (но полной а не express которая у меня установлена). Пусть 2008 или иных версий если 2005 уже нету. Кстати сперва предполагал что в форме предполагается и функция добавление дополнительных полей в таблицы но это наверное уже перебор ибо времени и так нету.



0



4 / 4 / 3

Регистрация: 29.01.2013

Сообщений: 12

13.06.2016, 23:08

10

Цитата
Сообщение от arts1
Посмотреть сообщение

Здесь еще вопрос возник — пытаюсь определить initializecomponent() в класе Form1 но что при компиляции вызывает ошибку что он уже определен (-яется).

InitializeComponent() определен в конструкторе. Переопределите конструктор.



0



138 / 7 / 1

Регистрация: 31.03.2015

Сообщений: 395

14.06.2016, 17:02

 [ТС]

11

Как это переопределить конструкор? Это метод кажется переопределяется вне конструктора — а в самом конструкторе уже вызывается. И непонятна схема файлов form1.cs form.designer.cs — какое распределение ролей между ними. Может все можно было включить в один файл? И касательно: менеджмент студио — может оставить экспрес скьюэл сервер, а к нему доставить полный вижуал студио если возможно лиш установить его шарп компонент (и это если верно предположение что такая связка возможна при присутствие лиш одной экспрес версии из двух приложений)?

Добавлено через 15 часов 9 минут
Немогу никак найти никакую полную версию менеджмент студио, ни всего скл сервера. Даже не вижу как выйти с этой ситуации. Разве конектится с MS Access, или с mysql — но в последнем случае надо уже вижуал студио (полный вариант), и хорошый редактор mysql.



0



Эксперт .NET

5459 / 4232 / 1208

Регистрация: 12.10.2013

Сообщений: 12,223

Записей в блоге: 2

14.06.2016, 17:29

12

Цитата
Сообщение от arts1
Посмотреть сообщение

Тихо сам с собою я веду беседу…

arts1, ваши вопросы, как бы это помягче сказать, выдают полное наличие отсутствия знаний. Совсем.
У вас два варианта:
1. Озвучить подробное ТЗ вашего приложения, как вы его видите, тогда кто-то вам, возможно, его и напишет.
2. Начать читать литературу, и тогда (через какое-то время) большая часть ваших вопросов отпадет сама собой.
Итак, ваш выбор?



0



Вежливость-главное оружие

233 / 234 / 86

Регистрация: 19.02.2013

Сообщений: 1,446

14.06.2016, 17:30

13

Вот пример Windows Forms приложения с использование Entity Framework 6.0 + Code First
LightDocumentManager.rar
А вот с использование ADO.NET
DatabaseClient.rar



0



138 / 7 / 1

Регистрация: 31.03.2015

Сообщений: 395

14.06.2016, 17:45

 [ТС]

14

Ну вы меня удивляете— у меня две экспрес версии вижуал студио С# и Slq server (management studio). Вместе они работать не могут. Для Шарп экспреса надо полную версию сервера которую я не могу найти. Здесь проблема не в знаниях — а в ограничение которое у меня выскакивает когда я хочу подсоединить бд.
И мне надо уже кодить а не какие то книги читать — Windows Forms Programming in C#: Chris Sells — думаю этого достаточно, как и ado.net троэлсена. Времени у меня нету — разве сделать форму и таблицы отдельно — а проверить работу системы где все необходимое по стоит.



0



Вежливость-главное оружие

233 / 234 / 86

Регистрация: 19.02.2013

Сообщений: 1,446

14.06.2016, 18:45

15

Цитата
Сообщение от arts1
Посмотреть сообщение

а в ограничение которое

чаво?

логи встудию



0



Эксперт .NET

5459 / 4232 / 1208

Регистрация: 12.10.2013

Сообщений: 12,223

Записей в блоге: 2

14.06.2016, 18:54

16

Цитата
Сообщение от arts1
Посмотреть сообщение

Здесь проблема не в знаниях

Сомневаюсь.

Цитата
Сообщение от arts1
Посмотреть сообщение

Windows Forms Programming in C#: Chris Sells — думаю этого достаточно, как и ado.net троэлсена.

Прочитав хотя бы одну из них, таких вопросов бы просто не было.

Цитата
Сообщение от arts1
Посмотреть сообщение

непонятна схема файлов form1.cs form.designer.cs — какое распределение ролей между ними. Может все можно было включить в один файл?

Так что учиться, учиться и еще раз учиться. Либо давайте ТЗ.



0



138 / 7 / 1

Регистрация: 31.03.2015

Сообщений: 395

14.06.2016, 19:02

 [ТС]

17

Ну что вы- я вам про одно а вы про другое. form1.cs form.designer.cs — да с формами не работал — но не думаю что у меня такая сложная форма будет — чтобы в процессе с этим не разобраться.
Уже вам не один раз писал о проблеме — не возможно подсоединить бд в вижуал студио экспрес 2005. Может 2008 не имеет этого ограничения — хотя вряд ли.



0



Эксперт .NET

5459 / 4232 / 1208

Регистрация: 12.10.2013

Сообщений: 12,223

Записей в блоге: 2

14.06.2016, 19:06

18

Цитата
Сообщение от arts1
Посмотреть сообщение

вижуал студио экспрес 2005.

arts1, 1. Есть 2015 Community, полностью бесплатная.
2. В сети достаточно образов нормальной Студии MVS 2010 Ultimate ( я как раз ей и пользуюсь).
Зачем вам все это старье? Минимум 2010, мне она больше всего нравится. Скачайте, установите и пользуйтесь на здоровье.



0



138 / 7 / 1

Регистрация: 31.03.2015

Сообщений: 395

15.06.2016, 00:17

 [ТС]

19

Так мне не экспрес сервер надо а не вижуад студио.

Добавлено через 1 час 52 минуты
Ну если профес. версия вижуал студио может работать с экспрес версией sql сервера то вы подскажите. У меня проблема с местом на диске C. Потому просто установить не получится. Хотя вот видел версию сервера enterprise 2008 (triаl) может такой вариант подойдет к вижуал экспрес 2005. Вроде в выше приведеном предупреждение идет речь что надо сервер 2005 or later. Возле инсталятора есть и ссылка для совместимости версий 2005 и 2008. Хотя может в триал версии есть свои ограничение. В этом контексте задам еще два вопроса: можно ли в последнем варианте установить лиш менеджмент студио (ведь весь обьем больше 2 гб), и будет ли этого достаточно для создание и манипулирование бд? Возможно ли эту всю систему — сервер энтерпрайз 2008 и вижуал экспрес поместить в вирт. систему?

Добавлено через 3 часа 2 минуты
А если использовать ole db с подключением ms access — которая с вижуал экспрес работает — думаю не трудно будет переделать для sql server если винформ будет с аксесом работать. Какие главные проблемы могут быть при добавление полей и отображение даных таблиц в форме? В первую очередь проблема null — так как будет использоваться лиш один переключатель/одна категория доходов и расходов то иные поля записи должны быть null — правильно ли это обработает ole db? Еще проблема формата даты — все другое текстовые и числовые значения. Еще непонятно как организировать категории и подкатегории — то есть групирование полей таблицы.



0



138 / 7 / 1

Регистрация: 31.03.2015

Сообщений: 395

16.06.2016, 16:05

 [ТС]

20

У меня появилось несколько вопросов: как извлечь Date и Time из datetimepicker. datetimepicker.value.date не помогает. И если передавать данные из пикера в sql server — datetimepicker.value.toString(). Тогда ведь невозможно будет задать условие where если это будет просто текстовое значение?? Еще возник вопрос — если в моей таблице будет заполнена лиш одна колонка для одной записи не считая колонки с датой (например категория1 доходов или категория2 доходов и т.д. и в каждой строке дата) то другие 6-7 полей будут пустые. Будут ли эти поля NULL, и как тогда применить функцию сумирования ко всему полю таблицы — через WHERE FIELD IS NOT NULL? И надо выводить результат просмотра таблицы лиш в обьекте DATAGRIDVIEW — если запрос будет применятся к сумированию части полей таблицы в некий период времени?



0



IT_Exp

Эксперт

87844 / 49110 / 22898

Регистрация: 17.06.2006

Сообщений: 92,604

16.06.2016, 16:05

20

Очень долго пытаюсь создать сервер-клиент приложение через сокеты и Tcp протокол. Запускаю на своем компе сервер и клиентов, отправка сообщений и ответ от сервера работают корректно, но если клиент запускает кто-то другой то вылетает исключение, что превышено время ожидания и сервер не дал ответа. Как быть?
Ниже прикладываю код сервера и клиента.

Сервер

        string hostName = Dns.GetHostName();
        IPHostEntry ipEntry = Dns.GetHostByName(hostName);
        IPAddress[] ipAdresses = ipEntry.AddressList;

        IPEndPoint ipEndPoint = 
             new IPEndPoint(IPAddress.Parse(ipAdresses[0].ToString()), 2017);

        Socket sListener = 
             new Socket(IPAddress.Parse(ipAdresses[0].ToString()).AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            sListener.Bind(ipEndPoint);
            sListener.Listen(10);

            while (true)
            {

                updateLog("Waiting for connection on [" + ipEndPoint + "]");

                Socket handler = sListener.Accept();
                updateLog("New user join the channel. [" + handler.LocalEndPoint + "]");

                string data = null;

                byte[] bytes = new byte[1024];
                int bytesRec = handler.Receive(bytes);

                data += Encoding.UTF8.GetString(bytes, 0, bytesRec);

                updateLog("Client[" + handler.LocalEndPoint + "]: " + data);

                // Отправка ответа клиенту.
                string reply = "Thanks for your query. We got it!";
                byte[] replyMSG = Encoding.UTF8.GetBytes(reply);
                handler.Send(replyMSG);

                handler.Shutdown(SocketShutdown.Both);
                handler.Close();
            }

Клиент

byte[] bytes = new byte[1024];

        IPAddress serverIp = IPAddress.Parse("Тут IP компьютера");

        IPEndPoint ipEndPoint = new IPEndPoint(serverIp, port);
        Socket sender = new Socket(serverIp.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

        sender.Connect(ipEndPoint);
        byte[] msg = Encoding.UTF8.GetBytes(msgTB.Text);
        int bytesSend = sender.Send(msg);
        int bytesRec = sender.Receive(bytes);

        logTB.AppendText("Server: " + Encoding.UTF8.GetString(bytes, 0, bytesRec));

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

Понравилась статья? Поделить с друзьями:
  • Клиент рабочих папок в windows 10 что это
  • Клиент рабочих папок в windows 10 можно ли отключить
  • Клиент почты не обнаружен windows 7
  • Клиент подключения к удаленному рабочему столу windows 10
  • Клиент отслеживания изменившихся связей что это за служба windows 10