• 8574
  • 0
  • 1
Нравится блог?
Подписывайтесь!

Клиент-сервер на WinSock

Привет всем юным кодерам. Сегодня я расскажу о написании клиент серверного приложения на winapi библиотеке Winsock.
Как известно клиент-серверное приложение состоит из клиента и сервера. Клиент у нас будет программой визуальной, т.е. это центр управление, с которого мы будет посылать команды не визуальному серверу, который будет из анализировать и выполнять.
Что касается компилятора то я буду использовать Borland c++. (Я уже вижу, как в меня летят тухлые помидоры =)).
Начнем с простого – клиент.
Как ты помнишь это программа визуальная. На главной форме должны присутствовать такие компоненты:
Текстовое поле для ввода IP адреса(Edit).
Кнопки для подключение и отключения сервера(Button).
Кнопки для управлением сервера (Button). (На ваш вкус)
Еще можно добавить одно многострочное поле для информирования пользователя о состоянии соединения(Memo).

http://stranger.nextmail.ru/ex.html

Да, и не забудь подключить библиотеку:

#include

Первое что бы сделаем это подключение к серверу.

{
WSADATA wsd;
If (WSAStartup(MAKEWORD(1,1), &wsd) != 0) // загрузка библиотеки и проверка на //ошибки
{
// если ошибки есть, то проинформировать юзера
Form1->Memo1->Lines->Add("Error load WinSock");
return ;
}
// создание нового потока 
HANDLE hThread;
DWORD idThread;
hThread = CreateThread(NULL, 0, Thread, 0, 0, & idThread);
}

Что тут происходит? Да пока ничего. Мы просто загрузили библиотеке функцией 
WSAStartUp и создали новый поток, в нем мы уже будет создавать сокет и соединяться с сервером. В первом аргументе функции WSAStartUp мы указали MAKEWORD(1,1) т.е. версия библиотеки 1. Второй аргумент это указатель на структуру WSADATA. В ней будет храниться информация о библиотеки.
Итак, поток создан, теперь можно работать с ним.
Сначала нужно объявить глобально следующие переменные
SOCKET sClient;
int ret, i;
struct sockaddr_in server;
struct hostent *host = NULL;
bool status;

По ходу дела мы разберемся, что к чему.
А вот и сам поток, что бы программа правильно проинициализировала его, поток нужно добавить до того кода, что я привел выше.

DWORD WINAPI Thread(LPVOID lpParam) 
{

char* szServerName= new char;
szServerName="12345";
szServerName=strcpy(szServerName,(Form1->Edit1->Text.c_str()));

//создание сокета
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sClient == INVALID_SOCKET)
{
MessageBox(0, "Can't create socket", "Error", 0);
return 1;
}
//заполненеие структуры sockaddr_in
server.sin_family = AF_INET;
server.sin_port = htons(7070);
server.sin_addr.s_addr = inet_addr(szServerName);

//само соеденение

if (connect(sClient, (struct sockaddr *)&server,
sizeof(server)) == SOCKET_ERROR)
{

Form1->Memo1->Lines->Add("connect failed");
return 1;
}
char szRecvBuff[1024];
//принимаем данные
ret = recv(sClient, szRecvBuff, strlen(szRecvBuff), 0);
if (ret == SOCKET_ERROR)
{

Form1->Memo1->Lines->Add("recv failed");
}
// выводим данные
String q=szRecvBuff;
Form1->Memo1->Lines->Add(q);
status=true;

}
Перед основным кодом мы заносим в переменную szServerName значение текстового поля, в который пользователь введет ip адрес.
Сначала создаем сокет функцией socket.
Первый аргумент это семейство протоколов.
Нам нужен TCP, так что указываем AF_INET.
Второй аргумент этот спецификация для сокета, т.е. что мы с ним будем делать.
Нам нужно установить соединение и передать данные, поэтому указываем SOCK_STREAM.
Третий аргумент собственно сам протокол. IPPROTO_TCP соответствуют протоколу TCP.
Теперь нам нужно заполнить структуру типа sockaddr_in. В ней храниться информация о созданном сокете. Напомню что переменую (server) данного типа (sockaddr_in) мы объявили глобально. В процессе заполнения структуры нам встречается функция htons.
Она осуществляет перевод целого короткого числа из порядка байт, принятого на компьютере, в сетевой порядок байт.
Ну и само соединение. Функция connect. Первый параметр это созданный сокет.
Второй – указатель на структуру типа sockaddr_in третий размер этой структуры.
Если все пройдет успешно, то можно принять данные функцией recv и вывести в текстовое поле. Первый аргумент функции recv это созданный сокет, второй – буфер который мы предварительно создали, третий аргумент это размер буфера (функция strlen возвращает размер строки). И, наконец, последний аргумент это флаги, определяющие метод получения. Они нам не нужны, поэтому указываем 0.
В конце кода мы приводим логическую переменную status в значение true. Это нужно чтобы впоследствии избежать ненужного повторного подключения.
Кстати, так как мы работаем с классно string в начале файла нужно добавить 

#include .
Мы научились соеденяться с сервером, теперь нужно добавить функцию отправки команд.
Для этого мы напишам функцию GetComand, ее единственным аргументом будет команда которую нужно отправить.
/
void GetCommand(char* str){ /
//если мы подлючены то…
if(status==true)
{
char* szMessage= new char;
szMessage="0000";
// копируем в буферв вписаную нами команду
strcpy(szMessage, str);
// отправка команды
ret = send(sClient, szMessage, strlen(szMessage), 0);
if (ret == SOCKET_ERROR)
{
} Form1->Memo1->Lines->Add("send failed");

//получение ответа
char szRecvBuff[1024];
ret = recv(sClient, szRecvBuff, 1024, 0);
if (ret == SOCKET_ERROR)
{

Form1->Memo1->Lines->Add("recv failed");
}
String q=szRecvBuff;
//вывод ответа
Form1->Memo1->Lines->Add(q);
}
else
{
//если мы не подключены то осведомить пользователя
Form1->Memo1->Lines->Add("The server is not connected");

}
}

Сначала происходит проверка переменной status. Если она равна true то мы подключены и можно выполнять дальнейший код.
А далее мы копируем в буфер szMessage значение переменной str. 
И потом отправляем это значение серверу функцией send;
Первый ее аргумент это сокет второй это буфер третий размер буфера.
После этого мы получаем ответ функцией recv он аналогична send.
После этого выводим ответ в текстовое поле.
Ответы мы будем формировать сами при разработке сервера.
Замечу, что в коде есть проверка на ошибки.
Функция готова к использованию.
Например при нажатии на кнопку «Перезагрузка» нужно выполнить такой код:

{
GetCommand("RB");
}

Кстати смотри не запутайся в командаъ т.к. в сервера из нужно будет анализировать.
Вот мои:
«Поменять кнопки мыши» -«AMB»
«Блокировать стол» - «BT»
«Сообщение» - «MSG»
Итак, простейший клиент готов. Подведем итог.
Клиент может подключаться к серверу отправлять и принимать команды. Пока это все.

Теперь можно приступить к разработке сервера.
Как я уже говорил сервер это программа не визуальная.
В нем, как и в клиенте нужно загрузить бибилиотеку создать сокет и подключиться.
Так же мы встретимся с привязкой к локльному адресу (функция bind) и прослушиванию порта (функция listen). 
Опять же глобально нужно объявить следущие переменные:

SOCKET sServerListen, sClient;
struct sockaddr_in localaddr,
clientaddr;
HANDLE hThread;
DWORD dwThreadId;
int iSize;
WSADATA wsaData;

Итак начнем вот код главной функции:

{
//загрузка библиотеки
WSAStartup(MAKEWORD(1, 1), &wsaData);
Создание сокета
sServerListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
заполнение струтуры адреса
localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
localaddr.sin_family = AF_INET;
localaddr.sin_port = htons(7070);
//привязка адреса к сокету
bind(sServerListen, (struct sockaddr *)&localaddr,sizeof(localaddr));
//простушиваниче порта 
listen(sServerListen, 2);
while (1)
{
iSize = sizeof(clientaddr);
//разрешение на соеденение
sClient = accept(sServerListen, (struct sockaddr *)&clientaddr,&iSize);
//создание потока для приема и отправки данных
hThread = CreateThread(NULL, 0, ClientThread,(LPVOID)sClient, 0, &dwThreadId);
CloseHandle(hThread);
}
closesocket(sServerListen);
return 0;
}

До заполнения структуры адреса все идет так же. После этого вызываеться функция bind.
Она служит для связи сокета с адресом.
Т.е. первым аргументом мы передали сокет, вторым указатель на структуру с адресом, третим – размер структуры.
Теперь сокет готов к работе и можно прослушивать порт на соеденение со стороны клиента.
Функция listen. У нее два параметра. Сокет и кол-во клиентов которые могут подключиться к серверу. Т.к. сервер должен постоянно находиться в памяти и принимать соеденения, то мы запускаем бесконечный цикл.
Функция accert проверяет подключающийся клиент и разрешает соеденения.
Первый параметр это сокет. Второйуказатель на структуру типа sockaddr ну и третий размер структуры. sClient будет указателем на новый сокет.
После этого создаем новый поток для обмена данными.
Вот этот поток:

DWORD WINAPI ClientThread(LPVOID lpParam)
{

SOCKET sock=(SOCKET)lpParam;

string a="";
char szRecvBuff[1024],szSendBuff[1024] ;
int ret;
strcpy(szSendBuff,"Server connect!");
send(sock, szSendBuff, sizeof(szSendBuff), 0);

while(1)
{
//Получаем данные
ret = recv(sock, szRecvBuff, 1024, 0);

if (ret == 0)
break;
else if (ret == SOCKET_ERROR)
{
MessageBox(0, "Recive data filed", "Error", 0);
break;
}
szRecvBuff[ret] = 'слеш0';
//записываем в стринговую переменную для дальнейшей обработки
a=szRecvBuff;
//Проверка команд 
//если команда перезагрузки то…
if(a=="RB")
{
ExitWindowsEx(EWX_REBOOT, 0);
strcpy(szSendBuff,"Computer reboot =) ");
send(sock, szSendBuff, sizeof(szSendBuff), 0);

}
else
//если команда поменять кнопки мышки то…
if(a=="AMB")
{

SwapMouseButton(false);

strcpy(szSendBuff,"The buttons on the mouse were displaced");
send(sock, szSendBuff, sizeof(szSendBuff), 0);
}
Else
//если команда для вывода сообщения то…
if(a=="MSG")
{
MessageBox(0, " It is time to you to be hung up", "Error", 0);
strcpy(szSendBuff," The message is sent");
send(sock, szSendBuff, sizeof(szSendBuff), 0);

}
Else
//если команда для блокирования стола
if(a=="BT")
{
HWND h=GetDesktopWindow(); 
EnableWindow(h, FALSE);
strcpy(szSendBuff,"The working table is blocked");
send(sock, szSendBuff, sizeof(szSendBuff), 0);

}

}
Сначала мы передаем клиенту сообщение о том, что сервер успешно подключен и готов к работе. Далее мы получаем команды и записываем полученную команду в стринговую переменную (не забудь добавить #include ) для того, что бы дальше ее проанализировать. Команд у нас не много поэтому здесь происходит проверка условием.
Надеюсь, смысл команд понятен. Замечу, что после выполнения каждой команды мы посылаем клиенту уведомление об успешности выполнения. Например, вот участок кода:

if(a=="BT")
{
HWND h=GetDesktopWindow(); 
EnableWindow(h, FALSE);
strcpy(szSendBuff,"The working table is blocked");
send(sock, szSendBuff, sizeof(szSendBuff), 0);

}

После того как стол заблокирован мы отправляем клиенту сообщение ,"The working table is blocked" – что означает рабочий стол заблокирован.
Все мы подготовили простейший макет клиент – сервера и даже внедрили некоторые функции. Чтое еще можно зделать? Все информация которую мы передаем в winsock передаеться байтами поэтому можно передать любой файл. Например скриншот экрана.
Если подумать, то можно организовать даже потоковое видео. Вот примерный алгоритм

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

©StraNger
Cпециально для hackzona.ru
03.02.2009

 

Уроки для хакеров и программистов

huzrus
3 февраля 2013, 21:50
1253

Загрузка...
Loading...

Комментарии

Оставьте свой комментарий

Спасибо за открытие блога в Yvision.kz! Чтобы убедиться в отсутствии спама, все комментарии новых пользователей проходят премодерацию. Соблюдение правил нашей блог-платформы ускорит ваш переход в категорию надежных пользователей, не нуждающихся в премодерации. Обязательно прочтите наши правила по указанной ссылке: Правила

Также можно нажать Ctrl+Enter

Популярные посты

Нет ничего более вдохновляющего, чем видеть стада сайгаков. Не зря их называют «киелі»

Нет ничего более вдохновляющего, чем видеть стада сайгаков. Не зря их называют «киелі»

В начале 2000-х годов в уральской популяции оставалось только 2500 сайгаков. Сейчас благодаря охране от браконьеров их численность выросла до 100 тысяч.
theYakov
17 июля 2017 / 17:55
  • 8660
  • 3
Ресторанный консенсус в Казахстане: мужчина платит всегда

Ресторанный консенсус в Казахстане: мужчина платит всегда

Ресторанный консенсус в этой стране таков, что если речь идет именно о свидании, то оплачивает его на 100% из 100 именно мужчина. Пытаться его расшатать - это достаточно дорогое удовольствие.
convoluted
17 июля 2017 / 15:32
Решили рискнуть и обратиться к риэлторам. И этим людям мы доверяем свой ночлег?

Решили рискнуть и обратиться к риэлторам. И этим людям мы доверяем свой ночлег?

Звоним риэлтору, говорим, верните наши 15 000 тг, так как ваша клиентка нас кинула. По его словам, он вернуть деньги больше не может. Не имеет право.
decorus
17 июля 2017 / 14:48
  • 3051
  • 15
Польша – страна простых решений. Почему они смогли, а мы еще нет?

Польша – страна простых решений. Почему они смогли, а мы еще нет?

В Польше вообще очень много понятного и простого – инфраструктура, коммуникации и дороги прежде всего, льготы в образовании, поляки вообще получают его бесплатно. А урожай побольше нашего.
Shimanskaya
17 июля 2017 / 16:08
  • 3197
  • 37
Один из способов выиграть суд против коллекторов

Один из способов выиграть суд против коллекторов

Сегодня в своем посте я постараюсь рассказать, как выиграть суд против некоторых коллекторских компании в нашей стране. Чем отличается коллекторское агенство от обычного банка?
Advokot
18 июля 2017 / 15:31
  • 2676
  • 9
«Язык мой – враг мой», или 7 причин никогда не разговаривать с полицией

«Язык мой – враг мой», или 7 причин никогда не разговаривать с полицией

На этот раз пост очень важный и необходим к прочтению каждым! Не поленитесь и уделите время прочтению. Ни в коем случае, не разговаривайте с полицейскими до прихода вашего адвоката!
asselsabekova
18 июля 2017 / 14:19
  • 2725
  • 31
«Алматы – город, летящий под откос», или Кто заказал утку у российского блогера

«Алматы – город, летящий под откос», или Кто заказал утку у российского блогера

Некий блогер Сергей Никитский неустанно пишет о Казахстане, Астане, Экспо и посвящает два материала Алматы, причём подчёркнуто называет город Алма-Ата.
Langdon
19 июля 2017 / 15:44
  • 2593
  • 50
Госорганы, ответственные за жизни детей, хранят молчание. У них в отчетах все хорошо

Госорганы, ответственные за жизни детей, хранят молчание. У них в отчетах все хорошо

Вчера все информационные агентства страны передали сообщение, которое заставило забиться в ужасе сердца всех матерей страны. В мусорном контейнере города Сатпаев было обнаружено тело новорожденной девочки.
AliyaSadyrbaeva
19 июля 2017 / 11:06
  • 2082
  • 17
Книга, которая сэкономит вам 150 тысяч долларов и два года жизни

Книга, которая сэкономит вам 150 тысяч долларов и два года жизни

Автор утверждает, что программы МБА не дают никакого позитивного выхлопа, если ты уже не являешься владельцем или наследником прибыльного бизнеса. Знания МБА можно получить бесплатно, уверяет он.
Aks_Ras
19 июля 2017 / 16:28
  • 2168
  • 2