Сетевое программирование POSIX
Модератор: Olej
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Сетевое программирование POSIX
Именно так: "Сетевое программирование POSIX", потому что в Windows сокеты и API сокетов отличаются (не сильно) ... ну, и меня слабо занимает программирование Windows.
Организация сети в Linux и сетевое программирование в POSIX API я описывал здесь: Сетевое программирование в Linux. В основу этого текста (и примеров) лёг курс лекций, который я заказным образом подготовил и прочитал для группы программистов-разработчиков такой международной софтверной компании как GlobalLogic.
Очень рекомендую прочитать этот текст ... в нём, хоть и очень бегло:
- рассматривается сетевая подсистема пользовательского уровня, сокеты BSD для TCP, UDP, SCTP ...
- рассматривается сетевая подсистема Linux в ядре, где нет уже сокетов, а сетевые пакеты IP фигурируют как сокетные буфера...
- и, неожиданно для меня самого, получилась прозрачная система прохождения всего сетевого трафика от пользовательского уровня одного хоста, до пользовательского уровня другого: через сокеты транспортного уровня, до сокетных буферов kernel Linux, и далее до физической среды передачи...
Организация сети в Linux и сетевое программирование в POSIX API я описывал здесь: Сетевое программирование в Linux. В основу этого текста (и примеров) лёг курс лекций, который я заказным образом подготовил и прочитал для группы программистов-разработчиков такой международной софтверной компании как GlobalLogic.
Очень рекомендую прочитать этот текст ... в нём, хоть и очень бегло:
- рассматривается сетевая подсистема пользовательского уровня, сокеты BSD для TCP, UDP, SCTP ...
- рассматривается сетевая подсистема Linux в ядре, где нет уже сокетов, а сетевые пакеты IP фигурируют как сокетные буфера...
- и, неожиданно для меня самого, получилась прозрачная система прохождения всего сетевого трафика от пользовательского уровня одного хоста, до пользовательского уровня другого: через сокеты транспортного уровня, до сокетных буферов kernel Linux, и далее до физической среды передачи...
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: Сетевое программирование POSIX
Но тема возникла не об этом ... потому что то дело давнее, ему 2 года как...Olej писал(а): Организация сети в Linux и сетевое программирование в POSIX API я описывал здесь: Сетевое программирование в Linux.
Но ко мне обратились за помощью в совершенно конкретной задаче.
Задача интересная.
И оказалось, что в форуме совершенно нет темы относительно сетевого программирования.
Восполняем...
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: Сетевое программирование POSIX
Это известная задача "Быки и Коровы".Olej писал(а): Задача интересная.
Для неё есть online WEB-версия, в которую можно поиграть :
Там же изложены:
(потому что ко мне формулировка задачи попала в куда более внятном, горбатом изложении)Правила игры
Компьютер задумывает четырехзначное число.
Цифры в числе не повторяются, 0 может стоять на первом месте.
Игрок делает ходы, чтобы узнать это число.
В ответ на каждый ход компьютер показывает число отгаданных цифр, стоящих на своих местах (число быков)
и число отгаданных цифр, стоящих не на своих местах (число коров).
Пример:
Компьютер задумал 0834. Игрок походил 8134. Компьютер ответил: 2 быка (цифры 3 и 4) и 1 корова (цифра 8).
Задача в том, чтобы этот алгоритм реализовать в клиент-серверном варианте, когда к игровому серверу могут подключаться сколь угодно много клиентов, и с каждым из них будет вестись независимая игра.
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: Сетевое программирование POSIX
Прежде чем делать сетевой вариант, делаем локальный:Olej писал(а): Задача в том, чтобы этот алгоритм реализовать в клиент-серверном варианте, когда к игровому серверу могут подключаться сколь угодно много клиентов, и с каждым из них будет вестись независимая игра.
Код: Выделить всё
#include "common.h"
#include "bull_cow.c"
int main( int argc, char *argv[] ) {
int opt;
while ( ( opt = getopt( argc, argv, "p:v") ) != -1 )
switch( opt ) {
case 'v' :
debug = true;
break;
default :
return 1;
}
char ask[ MAXLINE ] = "\n";
do printf( "%s", bull_cow( ask ) );
while( fgets( ask, MAXLINE, stdin ) );
exit( EXIT_SUCCESS );
}
Код: Выделить всё
char* bull_cow( char* s ) {
static char numb[ LENGTH + 1 ] = {};
static bool init = false;
if( !init ) {
srand( (unsigned int)time( NULL ) );
init = true;
}
if( strrchr( s, '\n' ) ) *strrchr( s, '\n' ) = '\0';
if( 0 == strlen( s ) ) {
for( int i = 0; i < LENGTH; i++ )
numb[ i ] = ' ';
for( int i = 0; i < LENGTH; i++ ) {
char c;
do c = rand() % 10 + '0';
while( strchr( numb, c ) );
numb[ i ] = c;
}
if( debug ) printf( "[%05u] число: %s\n", getpid(), numb );
sprintf( s, "новая игра, вводите число:\n" );
return s;
}
bool ok = strlen( s ) == LENGTH;
for( int i = 0; ok && i < LENGTH; i++ )
ok = isdigit( s[ i ] );
if( !ok ) {
strcpy( s, "ошибочный ввод\n" );
return s;
}
int bull = 0, // на своих местах
cow = 0; // не на своих местах
for( int i = 0; i < LENGTH; i++ )
if( strchr( numb, s[ i ] ) != NULL ) cow++;
for( int i = 0; i < LENGTH; i++ )
if( s[ i ] == numb[ i ] ) bull++;
sprintf( s, "%d %d\n", bull, cow - bull );
return s;
}
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: Сетевое программирование POSIX
Olej писал(а): Сама логика вынесена в отдельную функцию
Код: Выделить всё
[olej@dell 74]$ ./local -v
число: 3675
новая игра, вводите число:
3724
1 1
3725
2 1
3765
2 2
3675
4 0
Код: Выделить всё
[olej@dell 74]$ ./local
новая игра, вводите число:
2345
1 1
3567
1 3
3675
4 0
новая игра, вводите число:
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: Сетевое программирование POSIX
Дальше превратить вариант локальный в сетевой довольно просто...Olej писал(а): Сама логика вынесена в отдельную функцию
Сервер:
Код: Выделить всё
[olej@dell 74]$ cat server.c
#include "common.h"
#include "bull_cow.c"
bool retrans( int sc ) {
char data[ MAXLINE ];
int rc = read( sc, data, MAXLINE );
if( rc > 0 ) {
if( debug ) printf( "[%05u] %s", getpid(), data );
bull_cow( data );
rc = write( sc, data, strlen( data ) + 1 );
if ( rc < 0 ) perror( "write data failed" );
return rc > 0;
}
if( rc < 0 )
perror( "read data failed" );
if( rc == 0 )
printf( "client close connection\n" );
return false;
}
int main( int argc, char *argv[] ) {
in_port_t port = FORK_PORT;
int opt;
while ( ( opt = getopt( argc, argv, "p:v") ) != -1 )
switch( opt ) {
case 'p' :
if( atoi( optarg ) > 0 ) port = atoi( optarg );
break;
case 'v' :
debug = true;
break;
default :
return 1;
}
int ls, rs;
struct sockaddr_in addr;
if( -1 == ( ls = socket( AF_INET, SOCK_STREAM, 0 ) ) ) {
printf( "create stream socket failed\n" );
return 1;
}
if( setsockopt( ls, SOL_SOCKET, SO_REUSEADDR, &rs, sizeof( rs ) ) != 0 ) {
printf( "set socket option failed\n" );
return 1;
}
memset( &addr, 0, sizeof( addr ) );
addr.sin_family = AF_INET;
addr.sin_port = htons( port );
addr.sin_addr.s_addr = htonl( INADDR_ANY );
if( bind( ls, (struct sockaddr*)&addr, sizeof( struct sockaddr ) ) != 0 ) {
printf( "bind socket address failed\n" );
return 1;
}
if( listen( ls, 25 ) != 0 ) {
printf( "put socket in listen state failed\n" );
return 1;
}
if( debug )
printf( "[%05u] waiting on port %u\n", getpid(), port );
while( true ) {
if( ( rs = accept( ls, NULL, NULL ) ) < 0 ) {
printf( "accept error\n" );
return 1;
}
pid_t pid = fork();
if( pid < 0 ) {
printf( "fork error\n" );
return 1;
}
if( pid == 0 ) { // дочерний серверный процесс
close( ls );
while( retrans( rs ) ); // обмен с клиентом
close( rs );
exit( EXIT_SUCCESS );
}
else close( rs );
}
exit( EXIT_SUCCESS );
}
Код: Выделить всё
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h> // расширение стандарта C99
#include <time.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define FORK_PORT 52000
#define MAXLINE 120
#define LENGTH 4
static bool debug = false;
Код: Выделить всё
#include "common.h"
int main( int argc, char *argv[] ) {
char sadr[ MAXLINE ] = "localhost";
in_port_t port = FORK_PORT;
int opt;
while ( ( opt = getopt( argc, argv, "p:v") ) != -1 )
switch( opt ) {
case 'p' :
if( atoi( optarg ) > 0 ) port = atoi( optarg );
break;
case 'v' :
debug = true;
break;
default :
return 1;
}
if( optind < argc ) strcpy( sadr, argv[ optind ] );
printf( "host: %s TCP port = %u\n", sadr, port );
int rc, ls;
if( ( ls = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 ) {
printf( "create stream socket failed\n" );
return 1;
}
struct sockaddr_in addr;
memset( &addr, 0, sizeof( addr ) );
addr.sin_family = AF_INET;
addr.sin_port = htons( port );
inet_aton( sadr, &addr.sin_addr );
if( ( rc = connect( ls, (struct sockaddr*)&addr, sizeof( struct sockaddr ) ) ) < 0 ) {
printf( "connect failed\n" );
return 1;
}
char ask[ MAXLINE ] = "\n";
do {
if( ( rc = write( ls, ask, strlen( ask ) + 1 ) ) <= 0 ) {
perror( "write data failed" );
break;
}
rc = read( ls, ask, MAXLINE );
if( rc < 0 ) {
perror( "read data failed" );
break;
}
if( rc == 0 ) {
printf( "server closed connection\n" );
break;
}
printf( "%s", ask );
} while( fgets( ask, MAXLINE, stdin ) );
close( ls );
exit( EXIT_SUCCESS );
}
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: Сетевое программирование POSIX
Поехали...Olej писал(а): Дальше превратить вариант локальный в сетевой довольно просто...
2 независимых клиента вперемешку работают с сервером:
Код: Выделить всё
[olej@dell 74]$ ./server -p32500 -v
[09336] waiting on port 32500
[09376]
[09376] число: 0951
[09376] 1234
[09386]
[09386] число: 8354
[09386] 1234
[09376] 1590
[09386] 3456
[09386] 3458
[09376] 0915
[09376] 0951
[09376]
[09376] число: 0748
[09376] 4780
[09376] 0748
[09376]
[09376] число: 2049
[09386] 8345
[09386] 8354
[09386]
[09386] число: 8915
^C
Код: Выделить всё
[olej@dell 74]$ ./client -p 32500 192.168.1.106
host: 192.168.1.106 TCP port = 32500
новая игра, вводите число:
1234
0 1
1590
0 4
0915
2 2
0951
4 0
новая игра, вводите число:
4780
1 3
0748
4 0
новая игра, вводите число:
server closed connection
Код: Выделить всё
[olej@dell 74]$ ./client -p 32500 192.168.122.1 -v
host: 192.168.122.1 TCP port = 32500
новая игра, вводите число:
1234
1 1
3456
1 2
3458
1 3
8345
2 2
8354
4 0
новая игра, вводите число:
server closed connection
Код: Выделить всё
[olej@dell own_LSD2_11]$ ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 5c:26:0a:03:73:e9 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.107/24 brd 192.168.1.255 scope global dynamic eno1
valid_lft 162768sec preferred_lft 162768sec
inet6 fe80::5e26:aff:fe03:73e9/64 scope link
valid_lft forever preferred_lft forever
3: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 58:94:6b:19:ef:28 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.106/24 brd 192.168.1.255 scope global dynamic wlp3s0
valid_lft 162770sec preferred_lft 162770sec
inet6 fe80::5a94:6bff:fe19:ef28/64 scope link
valid_lft forever preferred_lft forever
4: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000
link/ether 52:54:00:22:16:10 brd ff:ff:ff:ff:ff:ff
inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
valid_lft forever preferred_lft forever
5: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc fq_codel master virbr0 state DOWN group default qlen 1000
link/ether 52:54:00:22:16:10 brd ff:ff:ff:ff:ff:ff
- Вложения
-
- 74.tgz
- (39.37 КБ) 123 скачивания
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Re: Сетевое программирование POSIX
Туда же, в архив, вложен текст моей давней статьи "Много серверов хороших и разных", на основе которой сделана основная часть ... чтобы не объясняться.Olej писал(а): Поехали...
В статье продемонстрировано 7 (как минимум) схем, по которым можно строить сервера TCP, ... и подробно об этом было здесь в другой теме, см.
много серверов хороших и разных.
Кто сейчас на конференции
Сейчас этот форум просматривают: нет зарегистрированных пользователей и 6 гостей