Простейший сервер. Консольное приложение.
Создаём сокет. Привязываемся к порту 30000. Начинаем его прослушивать. При обращении клиента функция accept возвращает второй сокет, через который будем общаться с клиентом. А через первый сокет будем ждать других клиентов. Дочерний процесс, который будет дальше общаться с клиентом, наследует дескрипторы (нас интересует дескриптор "второго" сокета). после создания дочернего процесса получается, что на "второй" сокет есть два "указателя" - в родительском и дочернем процессах. т.е. можно отправлять данные клиенту из этих двух процессов. после создания дочернего процесса нужно закрыть в родительском процессе "указатель" на "второй" сокет, чтобы не повесить клиента( иначе, когда дочерний процесс завершит диалог и закроет сокет, в родительском процессе этот сокет будет открыт).
файл ikkp_server.c (серверная часть 1)
//компиляция C-Free
//cd "C:\Program Files (x86)\C-Free 5\mingw\bin"
//gcc.exe ikkp_server.c -lws2_32 -o ikkp_server.exe
//завершение по "CTRL_С"
//netstat -ano
//обращение клиента: telnet 127.0.0.1 30000
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include <windows.h>
//#define BUFSIZE 4096
#define BUFSIZE 100
void error(char * msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}
int read_in(int socket, char * buf, int len)
{
char * s = buf;
int slen = len;
int c = recv(socket, s, slen, 0);
while ((c > 0) && (s[c-1] != '\n')) {
s += c; slen -= c;
c = recv(socket, s, slen, 0);
}
if(c < 0)
return c;
else if (c == 0)
buf[0] = '\0';
else
s[c-1] = '\0';
return len - slen;
}
int open_listener_socket()
{
int s = socket(PF_INET, SOCK_STREAM, 0);
if (s == -1 )
error("Can't open socket");
return s;
}
void bind_to_port(int socket, int port)
{
struct sockaddr_in name ;
name.sin_family = PF_INET;
name.sin_port = htons(port); //30000
name.sin_addr.s_addr = htonl(INADDR_ANY); // inet_addr("127.0.0.1")
int reuse = 1;
if(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(int)) == -1)
error("Ne mogu ustanovit dlya soketa parametr povtornogo ispolzovaniya");
int c = bind (socket, (struct sockaddr *) &name, sizeof(name));
if (c == -1)
error("Ne mogu privyazatsya k soketu");
}
int say(int socket, char * s)
{
int result = send(socket, s, strlen(s), 0);
//MessageBox(NULL, "debug1", TEXT("Error"), MB_OK);
if (result == -1)
fprintf(stderr, "%s: %s\n", "Error pri obschenii s klientom.", strerror(errno));
return result;
}
int main(int argc, char * argv[]){
SetConsoleCP(1251); // На ввод
SetConsoleOutputCP(1251); //На вывод.
///////////////////// Что-то типа инициализации. без этого не работает
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
//////////////////////
int listener_d = open_listener_socket();
//printf("Opened handle %i\r\n", listener_d);
bind_to_port(listener_d, 30000);
if(listen(listener_d, 10) == -1 ) // без этой строки - ошибка на accept(....
error("Can't listen");
char buf[255];
char * istr;
struct sockaddr_in client_addr;
unsigned int address_size = sizeof(client_addr);
puts("Waiting for connection");
while(1){
int connect_d = accept(listener_d, (struct sockaddr *)&client_addr, &address_size); //тут жд�'м
//printf("Opened handle %i\r\n", connect_d);
int radix = 10; //система счисления
char buffer[20]; //результат
char * pp; //указатель на результат
pp = itoa(connect_d, buffer, radix);
char par1[32]; //параметр командной строки для дочернего процесса
sprintf(par1, "ikkp_server_child.exe %s", pp); // ==>> "ikkp_server_child.exe 132\0"
// MessageBox(NULL, par1, TEXT("Error"), MB_OK);
if (connect_d == INVALID_SOCKET){
int ern = WSAGetLastError();
printf("Ошибка %i", ern);
return 1;
}
///////// создаём дочерний процесс. Дескрипторы наследуются.
///////// Для продолжения общения с клиентом передаём сокет connect_d (число) в командной строке.
STARTUPINFO cif;
ZeroMemory(&cif,sizeof(STARTUPINFO));
cif.cb = sizeof(STARTUPINFO);
//cif.hStdError = hWritePipe; //не меняем
//cif.hStdOutput = hWritePipe; //
//cif.hStdInput = hReadPipe2; //
cif.dwFlags |= STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi;
if (CreateProcess("ikkp_server_child.exe",
// "ikkp_server_child.exe 132" //указал имя приложения в первом параметре, и продублировал во втором параметре.
par1,
NULL,NULL,
TRUE, // handles are inherited //Дескрипторы наследуются
0,
// переменные окружения
NULL,
NULL, &cif,&pi)==TRUE){
}
else
{
error("Err of create child process");
}
//Sleep(2000);
closesocket (connect_d); //закрываю, чтобы клиент не повис.
// Этот сокет был унаследован дочерним процессом.
// После завершения общения с клиентом в дочернем процессе его закрою.
}
//после завершения работы с сокетами нужно использовать WSACleanup()
return 0;
}
файл ikkp_server_child.c (серверная часть 2)
//компиляция C-Free
//cd "C:\Program Files (x86)\C-Free 5\mingw\bin"
//gcc.exe ikkp_server_child.c -lws2_32 -o ikkp_server_child.exe
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include <windows.h>
#define BUFSIZE 100
void error(char * msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}
int read_in(int socket, char * buf, int len)
{
char * s = buf;
int slen = len;
int c = recv(socket, s, slen, 0);
while ((c > 0) && (s[c-1] != '\n')) {
s += c; slen -= c;
c = recv(socket, s, slen, 0);
}
if(c < 0)
return c;
else if (c == 0)
buf[0] = '\0';
else
s[c-1] = '\0';
return len - slen;
}
int say(int socket, char * s)
{
int result = send(socket, s, strlen(s), 0);
//MessageBox(NULL, "debug1", TEXT("Error"), MB_OK);
if (result == -1)
fprintf(stderr, "%s: %s\n", "Error pri obschenii s klientom.", strerror(errno));
return result;
}
int main(int argc, char * argv[]){
SetConsoleCP(1251); // На ввод
SetConsoleOutputCP(1251); //На вывод.
/////////////////////
char * str_socket = argv[1];
int connect_d;
//char str[256] = "12345"; //исходное число в виде строки
connect_d = atoi(str_socket); //результат
char buf[255];
// FILE * out1 = fopen("q1.txt", "a");
// fprintf(out1, "\r\n");
// fprintf(out1, str_socket);
// fclose(out1);
///////////////////Что-то типа инициализации. без этого не работает//
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
/////�'ыводим приветствие сервера/////////////////
if (say(connect_d, "Internet Protocol Server\r\nVersion 1.0\r\nPSP\r\n") == -1){
closesocket (connect_d);
// FILE * out1 = fopen("q1.txt", "a");
// fprintf(out1, "\r\nErr of posting message\r\n");
// fclose(out1);
error("Err of posting message");
}
/////Ждём запрос
//запрос может приходить с задержками и по частям
fd_set read_s; // Множество
int res;
int buffsize = 2023; //предполагаем , что запрос точно войд�'т в этот размер, не проверяем.
char buf[buffsize + 1];
char * bufC = buf;
struct timeval time_out; // Таймаут
FD_ZERO (&read_s); // Обнуляем множество
FD_SET (connect_d, &read_s); // �-аносим в него наш сокет
time_out.tv_sec = 10;
time_out.tv_usec = 500000; //Таймаут 10.5 секунды.
int bytesRcvd = 0;
u_long arg;
loop:
if (SOCKET_ERROR == (res = select (0, &read_s, NULL, NULL, &time_out) ) ){
return -1;
}
if (res!=0){
if (FD_ISSET (connect_d, &read_s)){ //т.к. сокет один, то можно не проверять
//MessageBox(NULL, "�"отов к чтению.", TEXT("notice"), MB_OK);
// https://club.shelek.ru/viewart.php?id=37
do
{ //MessageBox(NULL, "01", TEXT("notice"), MB_OK);
ioctlsocket(connect_d, FIONREAD, &arg); //сколько данных доступно для чтения
//printf("\r\nDostupno %i\r\n", arg);
if (arg == 0){
//MessageBox(NULL, "02", TEXT("notice"), MB_OK);
break;
}
if (arg > buffsize)
arg = buffsize;
bytesRcvd = recv(connect_d, bufC, arg, 0);
if (bytesRcvd > 0){
bufC[bytesRcvd] = '\0';
bufC = bufC + bytesRcvd;
//printf("%s", buf);
}
}
while (bytesRcvd > 0);
//Проверяю, приш�'л ли запрос полностью и оставшееся время .
// будет занесен адрес первой найденной подстроки
char *istr;
//Поиск строки
istr = strstr (buf, "\r\n\r\n");
if ( istr == NULL){
//время ещ�' есть. жд�'м
// FILE * out1 = fopen("q1.txt", "a");
// fprintf(out1, "\r\n2\r\n");
// fprintf(out1, buf);
// fclose(out1);
goto loop; // :)
}else{
// парсим запрос ...
//отправляем ответ
say(connect_d, "_�-апрошенная информация_\r\n");
}
}
}else{
// if ( (time_out.tv_sec == 0) && (time_out.tv_usec == 0) ){
// say(connect_d, "Time_out\r\n"); // клиент не готов, не зачем и отправлять
// }
// error("Клиент не готов к чтению");
}
closesocket (connect_d);
WSACleanup();
return 0;
}
файл client_.c (клиентская часть)
//компиляция C-Free
//cd "C:\Program Files (x86)\C-Free 5\mingw\bin"
//gcc.exe client_.c -lws2_32 -o client_.exe
#define _WIN32_WINNT 0x0501 //иначе - ошибка "Undefined reference to getaddrinfo"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#define BUFSIZE 100
////#pragma comment(lib, "Ws2_32.lib")
void error(char * msg)
{
fprintf(stderr, "%s: %s\n", msg, strerror(errno));
exit(1);
}
int open_socket(char * host, char * port)
{
struct addrinfo * res;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if(getaddrinfo(host, port, &hints, &res) == -1)
error("Не могу определить адрес");
int d_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if(d_sock == -1)
error("Не могк открыть сокет");
int c = connect(d_sock, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
if(c == -1)
error("Не могу подключиться к сокету");
return d_sock;
}
int say(int socket, char * s)
{
int result = send(socket, s, strlen(s), 0);
//MessageBox(NULL, "debug1", TEXT("Error"), MB_OK);
if (result == -1)
fprintf(stderr, "%s: %s\n", "Ошибка при общении с сервером", strerror(errno));
return result;
}
int main(int argc, char * argv[]){
SetConsoleCP(1251); // На ввод
SetConsoleOutputCP(1251); //На вывод.
///////////////////// Что-то типа инициализации. без этого не работает
WORD wVersionRequested;
WSADATA wsaData;
int err;
/* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
printf("WSAStartup failed with error: %d\n", err);
return 1;
}
//////////////////////
int d_sock;
d_sock = open_socket("127.0.0.1", "30000");
//Sleep(2000); //костыль, чтобы сокет успел подготовиться к чтению/записи не нужен, работает time_out в select()
// sprintf(buf, "GET /%s http/1.1\r\n", argv[1]);
//MessageBox(NULL, "11", TEXT("notice"), MB_OK);
// say(d_sock, buf);
// say(d_sock, "Host: localhost\r\n\r\n");
// при подключении клиента к серверу, последний может отправить шапку-приветствие, а может не отправить.
//а дальше сервер жд�'т запрос
// https://club.shelek.ru/viewart.php?id=37
fd_set read_s; // Множество
//fd_set write_s; // Множество
int res;
int buffsize = 255;
char buf[buffsize + 1];
struct timeval time_out; // Таймаут
FD_ZERO (&read_s); // Обнуляем множество
//FD_ZERO (&write_s); // Обнуляем множество
FD_SET (d_sock, &read_s); // �-аносим в него наш сокет
//FD_SET (d_sock, &write_s); // �-аносим в него наш сокет
time_out.tv_sec = 2; time_out.tv_usec = 500000; //Таймаут 2.5 секунды.
int bytesRcvd = 0;
u_long arg;
if (SOCKET_ERROR == (res = select (0, &read_s, NULL, NULL, &time_out) ) )
// select (0, &read_s, &write_s, NULL, &time_out)
error("SOCKET_ERROR");
if (res!=0){
// готов ли сокет к чтению?
if (FD_ISSET (d_sock, &read_s)){ //проверяем, были ли изменения по данному сокету
//MessageBox(NULL, "�"отов к чтению", TEXT("notice"), MB_OK);
// https://club.shelek.ru/viewart.php?id=37
do
{ //MessageBox(NULL, "01", TEXT("notice"), MB_OK);
ioctlsocket(d_sock, FIONREAD, &arg); //сколько данных доступно для чтения
//printf("\r\nDostupno %i\r\n", arg);
if (arg == 0){
//MessageBox(NULL, "02", TEXT("notice"), MB_OK);
break;
}
if (arg > buffsize)
arg = buffsize;
bytesRcvd = recv(d_sock, buf, arg, 0);
if (bytesRcvd > 0){
buf[bytesRcvd] = '\0';
printf("%s", buf);
}
}
while (bytesRcvd > 0);
}else
printf("\r\n Сервер не готов к чтению.\r\n");
// // // готов ли сокет к записи? готовность к записи может быть раньше готовности к чтению.
// // if ( !(FD_ISSET (d_sock, &write_s))) { //проверяем, были ли изменения по данному сокету
// // error("Сервер не готов к записи.");
// // } else
// // printf("\r\n Сервер готов к записи.\r\n");
}else error("Сервер не готов ни к чтению ни к записи");
//�"ЕЛАЕМ �-АПРОС
if (say(d_sock, "GET /index.php http/1.1\r\n") == -1){
closesocket (d_sock);
WSACleanup();
error("Ошибка отправки запроса");
}
if (say(d_sock, "Host: localhost\r\n\r\n") == -1){
closesocket (d_sock);
WSACleanup();
error("Ошибка отправки запроса");
}
// Ж�"ЁМ �"О�'О�'НОСТ� СОКЕТА �"ЛЯ ЧТЕН�Я
FD_ZERO (&read_s); // Обнуляем множество
FD_SET (d_sock, &read_s); // �-аносим в него наш сокет
time_out.tv_sec = 2; time_out.tv_usec = 500000; //Таймаут 2.5 секунды.
if (SOCKET_ERROR == (res = select (0, &read_s, NULL, NULL, &time_out) ) )
error("SOCKET_ERROR");
if (res!=0){
// готов ли сокет к чтению?
if (FD_ISSET (d_sock, &read_s)){ //проверяем, были ли изменения по данному сокету
do
{ //MessageBox(NULL, "01", TEXT("notice"), MB_OK);
ioctlsocket(d_sock, FIONREAD, &arg); //сколько данных доступно для чтения
//printf("\r\nDostupno %i\r\n", arg);
if (arg == 0){
break;
}
if (arg > buffsize)
arg = buffsize;
bytesRcvd = recv(d_sock, buf, arg, 0);
if (bytesRcvd > 0){
buf[bytesRcvd] = '\0';
printf("%s", buf);
}
}
while (bytesRcvd > 0);
}else
printf("\r\n Сервер не готов к чтению.\r\n");
}else error("Сервер не готов ни к чтению ни к записи");
///////////////////////////////////////////////////////////////////////////////////////
// // int bytesRcvd = recv(d_sock, buf, buffsize, 0);
// // BOOL l = TRUE;
// // if (SOCKET_ERROR == ioctlsocket (d_sock, FIONBIO, (unsigned long* ) &l) ) //перевод в неблокирующий режим
// // {
// // // Error
// // res = WSAGetLastError ();
// // return -1;
// // }
closesocket(d_sock);
//////////////////////
WSACleanup();
return 0;
}
https://stackoverflow.com/questions/2033608/mingw-linker-error-winsock
https://docs.microsoft.com/ru-ru/windows/desktop/WinSock/shared-sockets-2
https://docs.microsoft.com/ru-ru/windows/desktop/api/winsock2/nf-winsock2-wsaduplicatesocketa
https://metanit.com/cpp/c/7.3.php
http://vsokovikov.narod.ru/New_MSDN_API/Handles_objects/fn_duplicatehandle.htm
https://docs.microsoft.com/en-us/windows/desktop/procthread/creating-a-child-process-with-redirected-input-and-output
http://beej.us/guide/bgnet/translations/bgnet_A4_rus.pdf<
to be continued...