1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
| #define _WINSOCK_DEPRECATED_NO_WARNINGS
#pragma comment(lib,"Ws2_32.lib")
#include <stdio.h>
#include <winsock2.h> // Wincosk2.h повинен бути раніше windows!
#include <windows.h>
#include <clocale>
#define MY_PORT 777 // Порт, який слухає сервер
// Макрос для друку кількості активних користувачів
#define PRINTNUSERS if (nclients) printf ( "% d user on_line \n", nclients); else printf ( "No User on line \n");
// Прототип функції, обслуговуючої підключення
DWORD WINAPI ToClient(LPVOID client_socket);
// Глобальна змінна – кількість активних користувачів
int nclients = 0;
int main(int argc, char* argv[])
{
setlocale(LC_CTYPE, "rus");
char buff[1024]; // Буфер для різних потреб
printf("TCP SERVER DEMO \n");
// Крок 1 – Ініціалізація бібліотеки сокетів
// Так як повернута функцією інформація не використовується,
// Їй передається покажчик на робочий буфер, перетворений до покажчика
// На структуру WSADATA.
// Такий прийом дозволяє заощадити одну змінну, однак буфер
// Повинен бути не менше півкілобайта за розміром (структура WSADATA
// Займає 400 байт)
if (WSAStartup(0x0202, (WSADATA*)&buff[0]))
{
// Помилка
printf("Error WSAStartup % d \n", WSAGetLastError());
return -1;
}
// Крок 2 - створення сокета
SOCKET mysocket;
// AF_INET _ сокет Інтернету
// SOCK_STREAM - потоковий сокет (з установкою з'єднання)
// 0 - за замовчуванням вибирається TCP-протокол
if ((mysocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
// Помилка
printf("Error socket % d \n", WSAGetLastError());
WSACleanup(); // Деініціалізацію бібліотеки Winsock
return -1;
}
// Крок 3 - зв'язування сокета з локальною адресою
sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(MY_PORT);
local_addr.sin_addr.s_addr = 0; // Сервер приймає підключення
// На всі свої IP-адреси
// Викликаємо bind для зв'язування
if (bind(mysocket, (sockaddr*)&local_addr, sizeof(local_addr)))
{
// Помилка
printf("Error bind % d \n", WSAGetLastError());
closesocket(mysocket); // Закриваємо сокет
WSACleanup();
return -1;
}
// Крок 4 - очікування підключень
// Розмір черги - 0x100
if (listen(mysocket, 0x100))
{
// Помилка
printf("Error listen % d \n", WSAGetLastError());
closesocket(mysocket);
WSACleanup();
return -1;
}
printf("Ожидание подключения ... \n");
// Крок 5 - витягаємо повідомлення з черги
SOCKET client_socket; // Сокет для клієнта
sockaddr_in client_addr; // Адреса клієнта (заповнюється системою)
// Функції accept необхідно передати розмір структури
int client_addr_size = sizeof(client_addr);
// Цикл вилучення запитів на підключення з черги
while ((client_socket = accept(mysocket, (sockaddr*)&client_addr, &client_addr_size)))
{
nclients++; // Збільшуємо лічильник підключених клієнтів
// Намагаємося отримати ім'я хоста
HOSTENT* hst;
hst = gethostbyaddr((char*)&client_addr.sin_addr.s_addr, 4, AF_INET);
// Виведення відомостей про клієнта
printf(" + % s[% s] new connect!\n", (hst) ? hst->h_name : "", inet_ntoa(client_addr.sin_addr));
PRINTNUSERS
// Виклик нового потоку для обслуговування клієнта
// Для цього рекомендується використовувати _beginthreadex,
// Але, оскільки ніяких викликів функцій стандартної Сі-бібліотеки
// потік не робить, можна обійтися і CreateThread
DWORD thID;
CreateThread(NULL, NULL, ToClient, &client_socket, NULL, &thID);
}
return 0;
}
// Ця функція створюється в окремому потоці і обслуговує
// чергового підключеного клієнта незалежно від інших.
DWORD WINAPI ToClient(LPVOID client_socket)
{
SOCKET my_sock;
my_sock = ((SOCKET*)client_socket)[0];
char buff[20 * 1024];
#define sHELLO "Hello, Student \r \n"
// Відправляємо клієнту вітання
send(my_sock, sHELLO, sizeof(sHELLO), 0);
// Цикл луна-сервера: прийом рядка від клієнта і повернення її клієнту
int bytes_recv;
while ((bytes_recv = recv(my_sock, &buff[0], sizeof(buff), 0)) && bytes_recv!= SOCKET_ERROR) send(my_sock, &buff[0], bytes_recv, 0);
// Якщо ми тут, то стався вихід з циклу через
// Повернення функцією recv помилки - з'єднання з клієнтом розірвано
nclients--; // Зменшуємо лічильник активних клієнтів
printf(" - disconnect \n");
PRINTNUSERS
// Закриваємо сокет
closesocket(my_sock);
return 0;
} |