Перейти из форума на сайт.

НовостиФайловые архивы
ПоискАктивные темыТоп лист
ПравилаКто в on-line?
Вход Забыли пароль? Первый раз на этом сайте? Регистрация
Компьютерный форум Ru.Board » Компьютеры » Прикладное программирование » Несколько вопросов по сетевому программированию на С++ (азы)

Модерирует : ShIvADeSt

 Версия для печати • ПодписатьсяДобавить в закладки
Страницы: 1 2

Открыть новую тему     Написать ответ в эту тему

Wc3Exp

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Прочитал на геймдеве серию статей по сетевому программированию (только азы), как что устроено и кк работает. Всё понял, впринципе я как-то так это всё и представлял, но парочка вопросов всётаки появилось:...
 
1. Как правильно расположить "по старшинству" следующие понятия: порт физический (rj45), порт программный и сокет.
 Как я представляю: Физический порт "имеет" 65000+ программных портов, а дальше... Сколько сокетов может быть вообще или их количество ограничено только uint long? 1 сокет можно биндить на 1 программный порт, но 1 программный порт может обслуживать больше чем 1 сoкет одновременно, или как?
 
2. Организация взаимодействия "клиент-сервер" tcp/ip... Сервер слушает порт через сокет А. Клиент посылает запрос. Сервер принимает пакет и создаёт новый сокет В для нового клиента и отвечает клиенту через него.
 Собственно 3 момента:
 2.1 На какой АДРЕС сервер отсылает пакет через сокет В, или это всё "привязывается" при создании сокета или как узнаётся?
 2.2 Чтобы принять ответ сервера, клиент тоже должен слушать сокетом порт? Или сокет В изначально будет "нацелен" на порт клиента?
 2.3 Когда клиент постоянно шлёт данные на сервер, то каждый пришедший пакет создаёт новый сокет? Не логично както. Или как клиенту узнать ид сервеного сокета В, созданного для этого клиента?

Всего записей: 39 | Зарегистр. 01-01-2011 | Отправлено: 17:44 10-01-2011
Rudia



Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Видать плохая статья, раз ничего не понятно.
1. Зависит от операционной системы.
Насчет пункта 2, советовал бы почитать про udp и tcp - сокеты.
2.1 В tcp-протоколе клиент устанавливает подключение с сервером, т.е. для каждого клиента создается слейв-сокет на сервере. Т.е. для каждого клиента - отдельная "линия связи". В udp-протоколе на сервере всего один сокет, но при каждом получении пакета мы может определить адрес отправителя и ответить ему.
2.2 Для соединения с сервером клиент создает свой сокет. Ему автоматически назначается какой-то порт(клиенту это не важно, он про это не думает). У клиента обычно всего 2 операции - записать в сокет, прочитать из сокета. Стандартные сокеты блокирующие, т.е. если клиент вызовет метод "прочитать", то он не вернет результат, пока не прочитает, либо пока сервер не разорвет соединение.
2.3 Ответ кроется в пункте 2.1. Схема взаимодействия клиент-сервер
tcp - подключение-запрос-ответ-запрос-......-ответ-отключение
upd - запрос-ответ-запрос-.....

Всего записей: 324 | Зарегистр. 13-09-2006 | Отправлено: 10:51 11-01-2011
akaGM

Platinum Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Wc3Exp
прально, достойный выбор, выбор настоящего... игрока
[C++ vs C#]
 
типа оффтоп...

Всего записей: 24121 | Зарегистр. 06-12-2002 | Отправлено: 11:46 11-01-2011
Wc3Exp

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
прально, достойный выбор, выбор настоящего... игрока

Я понимаю, что ржачно читать мои вопросы, но далеко не всем дано всё понять с 1го прочтения.
Rudia
2.1 уже прочитал про accept и struct sockaddr_in... Ну и с udp и tcp тоже кратко ознакомился.
 
2.2 про асинхронные сокеты тоже прочитал, вот только не понял почему они рекомендованы только для клиента. Одно дело "подвесить" клиента с 1 игроком, другое - сервер с множеством игроков.  
Моё предположение, что, как правило, серверная апликуха - консольная, значит у неё нету ГУИ, следовательно нету дескриптора окна ГУИ, на который "завязан" асинхронный сокет. Так? Если так, то как тогда решён вопрос с сервером?
А так с ответом клиенту я уже разобрался.
 
2.3 допустим я играю в игру по tcp. Я авторизировался через серверный порт А и сокет Ах (прослушиваемый олько), сервер для обмена со мной создаёт сокет Бх с каким портом? Думаю, что с портом: взятым из struct sockaddr_in после accept (быстрее всего), заранее "забитым" в код (мб мб)...
Так вот суть вопроса в том, что, сокет Бх будет закрыт сервером после единократой предачи-получения данных или когда я выйду из игры (отсоеденюсь от сервера)?
 
p.s. Сегодня привезут новую клаву и я буду эксперементировать. У нынешней 40% клавишь перестало работать после пролитого чая. Вобщем вопросы ещё будут...

Всего записей: 39 | Зарегистр. 01-01-2011 | Отправлено: 20:17 11-01-2011 | Исправлено: Wc3Exp, 20:24 11-01-2011
akaGM

Platinum Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Wc3Exp
дурак ты...
 
я тебя на жабу сажал (добра желая),  а уж а С++ и не предполагал...
правильный выбор (без всяких граф. смайликов) :)

Всего записей: 24121 | Зарегистр. 06-12-2002 | Отправлено: 22:59 11-01-2011
Wc3Exp

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
akaGM
Я пробую на С++ т.к. он мне знаком, а С# имеет немного другой синтаксис...  
Сначала буду пробовать как это всё вообще делается на знакомом синтаксисе, а в последствии никто не мешает перейти на C#... Я ещё только учусь, не более.

Всего записей: 39 | Зарегистр. 01-01-2011 | Отправлено: 23:06 11-01-2011
akaGM

Platinum Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Wc3Exp
самый простой ответ
С++ рулит...
тем более, что ты в курсах...
имхо, бери его, сиди на нём, и никаких сомнений...

Всего записей: 24121 | Зарегистр. 06-12-2002 | Отправлено: 23:21 11-01-2011
Wc3Exp

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Код:
 
SOCKET        client_socket[client_max] , server_socket;
sockaddr_in   ws_client_info[client_max] , ws_client_info;
 
client_socket[client_count] = accept(server_socket,????????,sizeof(sockaddr_in));
Не могу разобраться со вторым агрументом функции accept.  
Суть понимаю - вместо вопросов должна быть структура типа sockaddr_in (я так понимаю ws_client_info[client_count]) в которую записываются данные подключаемого компьютера.
 
Перепробовал несколько вариантов, но постоянно выкидывает разнообразные ошибки. Что правильно записать на место 2го аргумента?

Всего записей: 39 | Зарегистр. 01-01-2011 | Отправлено: 00:35 12-01-2011 | Исправлено: Wc3Exp, 00:37 12-01-2011
vlary



Platinum Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Wc3Exp  
sockaddr_in saddr;
accept(server_socket, (struct sockaddr *)  &saddr ,sizeof(sockaddr_in));
Вторым параметром ты должен передать ссылку на структуру типа sockaddr.


----------
Заслуженный SCOтовод, почетный SUNтехник и любитель Кошек

Всего записей: 17280 | Зарегистр. 13-06-2007 | Отправлено: 00:52 12-01-2011
Wc3Exp

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Спасибо, честно говоря уже запутался немного.
В таком виде всё работоспособно...

Код:
  client_socket[client_count] = accept(server_socket,(struct sockaddr*)&ws_client_info[client_count],&size);
Единственное что потребовало ссылку на ранее определённый размер структуры, иначе сорило ощибкой.

Всего записей: 39 | Зарегистр. 01-01-2011 | Отправлено: 01:08 12-01-2011
vlary



Platinum Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Wc3Exp Правильно, третий параметр - тоже ссылка.
SYNOPSIS
#include <sys/types.h>
#include <sys/socket.h>
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);


----------
Заслуженный SCOтовод, почетный SUNтехник и любитель Кошек

Всего записей: 17280 | Зарегистр. 13-06-2007 | Отправлено: 01:14 12-01-2011 | Исправлено: vlary, 01:16 12-01-2011
Wc3Exp

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
vlary
Так, хорошо... А что дальше делать? Как заставить серверное приложение обрабатывать эту тучу созданных сокетов? В примерах ничего не нашёл по этому моменту, в них дальше дестрой сокета и WSACleanup(), кстати что за функция, какой мусор она убирает?...  
 
Для ясности, я в качестве освоения хочу сделать простенький чат на консольном сервере и консольном клиенте...

Всего записей: 39 | Зарегистр. 01-01-2011 | Отправлено: 01:27 12-01-2011
vlary



Platinum Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Wc3Exp Обычно серверное приложение открывает сокет и слушает на нем. Когда приходит клиентский запрос, по accept открывается новый сокет, создается новый процесс или поток, которому передается этот сокет для работы с клиентом. Далее уже дочерний процесс/поток осуществляет прием-передачу данных между клиентом и сервером. После завершения обмена, либо отключения клиента, либо тайм-аута, сокет закрывают командой close или  shutdown. При завершении программы сервера закрывается слушающий сокет, и выполняется WSACleanup(), которая очищает буферы обмена, закрывает сокеты, которые забыли закрыть, и выгружает WS2_32.dl.  
Вот простой примерчик многопоточного TCP сервера: Ссылка

Всего записей: 17280 | Зарегистр. 13-06-2007 | Отправлено: 12:51 12-01-2011
Wc3Exp

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
vlary
Немного разобрался с потоками, написал простенькие, почти рабочие сервер и клиент, код пока индусский... но это пока обучаюсь.
 
Теперь такие вопросы возникли...
*Запущены и сервер и клиент.
1. Подсоединяюсь к серверу.
2. Сервер принимает коннект, создаёт сокет, создаёт поток и передаёт в него сокет.  
3. Из созданного потока сервер отсылает клиенту сообщение об успешном коннекте (оно доходит до клиента). Созданный поток зацикливается на приём сообщения от клиента (этот момент видимо неправильный т.к. происходит какой-то бред).
4. Клиент в отдельном потоке принимает сообщение и выводит на экран. Приём тоже зациклен и тоже много бреда.
5. (вопрос) Куда посылать сообщение от клиента к серверу? Неужто на тот серверный слушающий сокет, к которому коннектился клиент? Т.к. больше некуда, то полагаю, что на него. Ведь на самом сервере созданный аccept'ом сокер биндится на структуру с его данными. Видимо это как-то взаимосвязано. Тогда наверное нужно в отдельном потоке выставлять слушающий сокет на приём данных (именно приём а не коннект)?
 
Ну ещё... Я так понял, что функции send(...) и  recv(...) не блокируют программу, именно поэтому у меня получается много бреда, если её вызывать из бесконечного while(true)... Тогда как можно создать событие, что на какой-то сокет принял данные, или дорога только к асинхронным сокетам?

Всего записей: 39 | Зарегистр. 01-01-2011 | Отправлено: 04:51 13-01-2011 | Исправлено: Wc3Exp, 06:11 13-01-2011
Rudia



Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Wc3Exp
Вызвали connect - получили номер сокета, далее работа с send() и recv(). Если хоть одна из функций возвращает ошибку - closesocket() и далее по новой - socket(), connect().

Цитата:
Ну ещё... Я так понял, что функции send(...) и  recv(...) не блокируют программу, именно поэтому у меня получается много бреда, если её вызывать из бесконечного while(true)... Тогда как можно создать событие, что на какой-то сокет принял данные, или дорога только к асинхронным сокетам?

 Изучать функцию select().

Всего записей: 324 | Зарегистр. 13-09-2006 | Отправлено: 11:01 13-01-2011
vlary



Platinum Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Wc3Exp Ну, в данном случае, как и написал уже Rudia, нужно либо использовать функцию  select(), либо работать с событиями. Пример с использованием select() можно посмотреть здесь:
Ссылка

Всего записей: 17280 | Зарегистр. 13-06-2007 | Отправлено: 11:26 13-01-2011
Wc3Exp

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Что-то уже совсем ничего не понимаю. При попытке коннекта, со стороны клиента связь не устанавливается, но при этом на сервере accept успешно завершает свою функцию... Как так?
 
Добавлено:
Тестовое сообщение с сервера на клиент естественно не передаётся.

Всего записей: 39 | Зарегистр. 01-01-2011 | Отправлено: 01:11 15-01-2011
vlary



Platinum Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Wc3Exp Показывай, что делаешь, иначе - к телепатам. Обычно все учебные примеры работают на ура.


----------
Заслуженный SCOтовод, почетный SUNтехник и любитель Кошек

Всего записей: 17280 | Зарегистр. 13-06-2007 | Отправлено: 16:52 15-01-2011
Wc3Exp

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Не помещаю под тег //more// для удобства ковыряния.
 
Вот это код клиента:

Код:
#include "stdafx.h"
#pragma comment (lib, "ws2_32.lib")    
#include <iostream>
#include <winsock2.h>
 
// globals
    sockaddr_in s_inf;
    int len = sizeof(struct sockaddr_in);
    SOCKET s;
    HANDLE InThr;
    DWORD ThreadId;
    WSADATA wsa_dat;
    WSAEVENT sEvent;
    char IP[15];
// endglobals
 
DWORD WINAPI InThrWork(LPVOID lpParam)  
{
    SOCKET s = (SOCKET)lpParam;
    char ch[256];
    int lch = sizeof(ch)*2;
    int eventCode = 0;
    WSABUF inbuff;
    WSANETWORKEVENTS NetEvent;
 
    printf("%s","Thread start!\n"); //*
    while (true)  
    {
        inbuff.buf = ch;
        inbuff.len = lch;
        printf("%s","Input thread : waiting data!\n"); //*
        eventCode = WSAWaitForMultipleEvents(1,&sEvent,FALSE,INFINITE,FALSE);
        printf("%s","Input thread : event run!\n"); //*
        WSAEnumNetworkEvents(s,sEvent,&NetEvent);
        if((NetEvent.lNetworkEvents & FD_READ) && (NetEvent.iErrorCode[FD_ACCEPT_BIT]==0))
        {
            printf("%s","Input thread : READ event!\n"); //*
            WSARecv(s,&inbuff,1,0,0,NULL,NULL);
            printf("%s",inbuff.buf);
        }
        if((NetEvent.lNetworkEvents & FD_CLOSE) && (NetEvent.iErrorCode[FD_ACCEPT_BIT]==0))
        {
            printf("%s","Input thread : CLOSE event!\n"); //*
            ExitThread(ThreadId);
        }
    }
return 0;
}
    
int main (int argc, char* argv[])
{
    char ch[256];
    int lch = sizeof(ch)*2;
    WSABUF outbuff;
    unsigned long bl = 0;
 
 
    if ( WSAStartup(2,&wsa_dat) !=0 )
    {
        puts("WSAStartup - False.\n");
        exit (1);
    }
    
    if (wsa_dat.wVersion != 2)
    {
        puts("WSA Version - False.\n");
        WSACleanup ();
        exit (1);
    }
 
    s = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,NULL);
 
    if ((s==INVALID_SOCKET) | (s==SOCKET_ERROR))
    {        
        puts("SOCKET create - False.\n");
        exit (1);    
    }else
    {
        sEvent = WSACreateEvent();
        WSAEventSelect(s,sEvent,FD_READ | FD_CLOSE);
    }
 
 
    puts("Enter IP adress:");
    scanf("%15s",IP);
    if (strlen(IP)<7)
    {
        strcpy(IP,"127.0.0.1");
    }
 
    s_inf.sin_family = AF_INET;
    s_inf.sin_port = htons(1501);
    s_inf.sin_addr.s_addr = inet_addr(IP);
 
    if (WSAConnect(s,(struct sockaddr*)&s_inf,sizeof(s_inf),NULL,NULL,0,0)==SOCKET_ERROR)
    {
        printf("%s","Connection - False.\n");
    }else
    {
        InThr = CreateThread(NULL,0,InThrWork,(LPVOID)s,0,&ThreadId);
    }
 
    puts("====================\n");
    while(true)
    {
        scanf("%512s",ch);
        outbuff.buf = ch;
        outbuff.len = lch;
        WSASend(s,&outbuff,1,0,0,NULL,NULL);
        puts("--------------------");
    }
return 0;
}

 
А это код недосервера:
Код:
#define MAX_CLIENTS 10
 
#pragma comment (lib, "ws2_32.lib")    
#include <iostream>
#include <winsock2.h>
#include <string.h>
 
using namespace std;
 
// globals
    sockaddr_in ws_client_info[MAX_CLIENTS] , ws_server_info;
    SOCKET client_socket[MAX_CLIENTS] , server_socket;
    WSADATA     ws_dat;
    int client_count = 0;
    int len = sizeof(struct sockaddr_in);
    HANDLE InputTread;
    HANDLE EventSoc[MAX_CLIENTS];
    DWORD InputTreadId;
    HANDLE NewTreade[MAX_CLIENTS];
    DWORD TreadeId[MAX_CLIENTS];
    int Key[MAX_CLIENTS];
 
    WSABUF wsaBuff;
// endglobals
 
DWORD WINAPI client_work (LPVOID lpParam)
{
    int client_index = (int)lpParam - 1;
    SOCKET client_soc = client_socket[client_index];
    int index = 0;
//    char buff[512];
 
 
    wsaBuff.buf = "Connection complete.";
    wsaBuff.len = strlen("Connection complete.");
    WSASend(client_socket[client_index],&wsaBuff,1,0,0,NULL,NULL);
    puts("Treade start!\n");
/*    while(true)
    {
        WSAWaitForMultipleEvents(2,&EventSoc[client_index],false,WSA_INFINITE,false);
        if (WSARecv(client_soc,&wsaBuff,1,0,0,NULL,NULL)==SOCKET_ERROR)
        {
            closesocket(client_soc);
            break;
        } else {
            puts(buff);
            
            while(index<50)
            {
                if (Key[index]==1)
                {
                    if (client_socket[index]!=client_soc)
                    {
                        send(client_socket[index],buff,strlen(buff),0);
                    }
                }
            }
        }
    }*/
    return 0;
}
 
 
int main ()
{
    memset(&ws_client_info,0,len);
    memset(&ws_server_info,0,len);
 
    if (WSAStartup (2,&ws_dat) != 0)
    {
        cerr << "WSAStartup - False." << endl;
        exit (1);
    }
 
    if (ws_dat.wVersion != 2)
    {
        cerr << "WSA Version - False." << endl;
        WSACleanup ();
        exit (1);
    }
 
    server_socket = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,0);
 
    if (server_socket == INVALID_SOCKET)
    {        
        cerr << "Socket create - False."<< endl;
        exit (1);    
    }
 
    ws_server_info.sin_family = AF_INET;
    ws_server_info.sin_port = htons(1501);
    ws_server_info.sin_addr.s_addr =htonl(INADDR_ANY);
 
    if (bind(server_socket,(struct sockaddr*)&ws_server_info,sizeof(ws_server_info)) == SOCKET_ERROR)
    {
        cout << "Bind - False." << endl;
        WSACleanup ();
        exit (1);  
    }
 
    if (listen(server_socket,5)==SOCKET_ERROR)  
    {
        cout << "Listen-mode - False." << endl;
        exit (1);
    }
 
    cout << "Server online!\n" << endl;
 
    while (client_count < MAX_CLIENTS)
    {
        client_socket[client_count] = WSAAccept(server_socket,(struct sockaddr*)&ws_client_info[client_count],&len,NULL,0);
        if (client_socket[client_count] == INVALID_SOCKET)
        {
            WSACleanup ();
            return 0;
        }
        else  
        {
//            WSAEventSelect(client_socket[client_count],EventSoc[client_count],FD_READ);
//            bind(client_socket[client_count],(struct sockaddr*)&ws_client_info[client_count],len);
            cout << "Now client on server: " << client_count+1 << endl;
//            NewTreade[client_count] = CreateThread(NULL,0,client_work,(LPVOID)client_count,0,&TreadeId[client_count]);
            wsaBuff.buf = "Connection complete.";
            wsaBuff.len = strlen("Connection complete.");
            WSASend(client_socket[client_count],&wsaBuff,1,0,0,NULL,NULL);
            Key[client_count] = 1;
            client_count++;
        }
    }
    WSACleanup ();
return 0;
}

Всего записей: 39 | Зарегистр. 01-01-2011 | Отправлено: 20:39 15-01-2011 | Исправлено: Wc3Exp, 20:42 15-01-2011
vlary



Platinum Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Wc3Exp Ну вот сразу же: в обработчике

Цитата:
 int client_index = (int)lpParam - 1;  
и в главном потоке
Цитата:
NewTreade[client_count] = CreateThread(NULL,0,client_work,(LPVOID)client_count,0,&TreadeId[client_count]);  

Ты не правильно параметры передаешь и читаешь.
В сервере должно быть (LPVOID) &client_count, а в клиентском потоке  (int)*lpParam
Иначе обработчик вместо индекса сокета нового клиента получит хрен знает что.
 
 
 


----------
Заслуженный SCOтовод, почетный SUNтехник и любитель Кошек

Всего записей: 17280 | Зарегистр. 13-06-2007 | Отправлено: 21:51 15-01-2011 | Исправлено: vlary, 21:53 15-01-2011
Открыть новую тему     Написать ответ в эту тему

Страницы: 1 2

Компьютерный форум Ru.Board » Компьютеры » Прикладное программирование » Несколько вопросов по сетевому программированию на С++ (азы)


Реклама на форуме Ru.Board.

Powered by Ikonboard "v2.1.7b" © 2000 Ikonboard.com
Modified by Ru.B0ard
© Ru.B0ard 2000-2024

BitCoin: 1NGG1chHtUvrtEqjeerQCKDMUi6S6CG4iC

Рейтинг.ru