forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   C++ (http://forum.boolean.name/forumdisplay.php?f=22)
-   -   С++ Sockets (http://forum.boolean.name/showthread.php?t=20089)

Okay 16.11.2015 05:36

С++ Sockets
 
Суть простая. Сервер получает по 4 байта. То бишь при отправки "how are you?" получает 3 сообщения сразу, а не одно


Код:

#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <vector>
#include <string>
#include <Windows.h>

#pragma comment(lib, "ws2_32.lib")
using namespace std;
SOCKET Connections[512];
SOCKET Connect;

SOCKET Listen;
SOCKET RECV;

int ClientCount = 0;



struct Data
{
        int ID;
        char * Name;
       
};

vector<Data> Players;
vector<string> ReadyMessage;

void ReadMessage(char * buffer);
void SetClientName(char * name);
void DeleteClient(int getID);
bool IsConnected(int getID);
void Work(int getID);



void SetClientName(char * name)
{

        return;
}

void DeleteClient(int getID)
{
        std::cout << "EZ DISCONNECT!" << std::endl;
        int size = Players.size();
        for (int i = 0; i < size; i++)
        {
                if (Players[i].ID == getID)
                {
                        Players.erase(Players.begin() + i);
                }
        }
        Connections[getID] = NULL;
        closesocket(getID);
        return;
}

bool IsConnected(int getID)
{
        char * buffer = new char[1];
        int nSendBytes = send(getID, buffer, strlen(buffer), NULL);
        if (nSendBytes == SOCKET_ERROR)
        {
                DeleteClient(getID);
                return false;
        }
        else return true;
}

void Input()
{
        char * buffer = new char[512];
        while (true)
        {
               
                cin >> buffer;
                cout << "SENT: " << buffer << "[" << strlen(buffer) << "]" << endl;
                int size = Players.size();
                if (strcmp(buffer, "quit") == 0)
                {
                        exit(0);
                }
                else
                {
                        for (int i = 0; i < size; i++)
                        {
                                send(Players[i].ID, buffer, strlen(buffer), NULL);
                        }
                }       
        }
}


void Work(int getID)
{
        std::cout << "Thread created" << std::endl;
        char * buffer = new char[512];
        int result;
        string rMessage = "";
        while (IsConnected(getID) == true)
        {
                Sleep(50);

                result = recv(getID, buffer, sizeof(buffer), NULL);
               
                if (result > 0)
                {
                       
                        buffer[result] = '\0';
                        std::cout << "Message from " << getID << ": " << buffer << std::endl;
                        int size = Players.size();
                        for (int i = 0; i < size; i++)
                        {
                                send(Players[i].ID, buffer, strlen(buffer), NULL);
                        }
                        ZeroMemory(buffer, sizeof(buffer));
                        //std::cout << result << std::endl;
                }


        }

        closesocket(getID);
        return;
}



int main()
{
        setlocale(LC_ALL, "RU");
        WSADATA wsa;
        SOCKET s, new_socket;
        struct sockaddr_in server, client;
        int c;
        char *message;

        printf("\nInitialising Winsock...");
        if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
        {
                printf("Failed. Error Code : %d", WSAGetLastError());
                return 1;
        }

        printf("Initialised.\n");

        //Create a socket
        if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
        {
                printf("Could not create socket : %d", WSAGetLastError());
        }
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Input, NULL, 0, NULL);
        printf("Socket created.\n");

        //Prepare the sockaddr_in structure
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(2372);

        //Bind
        if (bind(s, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
        {
                printf("Bind failed with error code : %d", WSAGetLastError());
        }

        puts("Bind done");

        //Listen to incoming connections
        listen(s, 3);

        //Accept and incoming connection
        puts("Waiting for incoming connections...");

        c = sizeof(struct sockaddr_in);

        while (true)
        {
                Sleep(50);
                new_socket = accept(s, NULL, NULL);
                if (new_socket == INVALID_SOCKET)
                {
                        printf("accept failed with error code : %d", WSAGetLastError());
                }
                else
                {
                        Data newClient;
                        newClient.ID = new_socket;
                        //closesocket(new_socket);
                        cout << "Client with ID " << newClient.ID << " connected!" << endl;
                        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Work, (LPVOID)newClient.ID, 0, NULL);
                        Players.push_back(newClient);
                       
                }
        }
        getchar();

        closesocket(s);
        WSACleanup();


}


Жека 16.11.2015 06:05

Ответ: С++ Sockets
 
Проверь чему равно sizeof(buffer).

Okay 16.11.2015 06:42

Ответ: С++ Sockets
 
Цитата:

Сообщение от Жека (Сообщение 301488)
Проверь чему равно sizeof(buffer).

Thanks!
Ошибка была в
Код:

char * buffer = new char[512];
Изменил на
Код:

сhar buffer[512];

Жека 16.11.2015 08:04

Ответ: С++ Sockets
 
Можно сделать переменную
PHP код:

int buf_size 512

чтобы не дёргать sizeof(buffer).

Ещё - в функции Work есть строка
PHP код:

send(Players[i].IDbufferstrlen(buffer), NULL); 

подумалось, что strlen(buffer) всегда будет равна result. но для utf я не уверен. это тоже к вопросу о целесообразности каждый раз дёргать функцию.

Okay 16.11.2015 09:36

Ответ: С++ Sockets
 
Цитата:

Сообщение от Жека (Сообщение 301491)
Можно сделать переменную
PHP код:

int buf_size 512

чтобы не дёргать sizeof(buffer).

Ещё - в функции Work есть строка
PHP код:

send(Players[i].IDbufferstrlen(buffer), NULL); 

подумалось, что strlen(buffer) всегда будет равна result. но для utf я не уверен. это тоже к вопросу о целесообразности каждый раз дёргать функцию.

В принципе как вариант :) Даже не обратил внимание

Еще вопрос есть по обработке сообщений. То бишь нужно научить сервер разбирать сообщения по типам, почти как игровой. Сообщения типа "Я передвинулся", "Я нажал какую то кнопку" и тд

Думал парсить каждое сообщение, но по скорости не знаю как выйдет. Быть может есть способ легче?

Жека 16.11.2015 12:18

Ответ: С++ Sockets
 
Зависит от того, какой тип протокола хочешь - текстовый или бинарный.
Текстовый легко парсить, бинарный не пробовал, наверное тоже легко.
Про скорость - хз, зависит от количества команд в единицу времени.
Если команд не сильно много, то среди прочих временных затрат будет не заметно.

impersonalis 16.11.2015 13:47

Ответ: С++ Sockets
 
Код не читал @ сразу отвечал.
Кол-во принятых и прочитанных байт при асинхронном сокете tcp (и, надо полагать, udp - не работал с ним) никто не гарантирует. Надо постоянно проверять.
Ещё раз повторю: и для отправленного тоже. То есть вы должны убедиться, что отправлен весь указанный буфер, а не его часть. Соответственно, часть буфера возможно придётся "отправлять" (передавать в функцию отправки) ещё раз. Последнее - довольно частый баг, т.к. может не проявлять себя сколь угодно долго (пока сеть не будет забита => часть инфы не будет фактически отправлена с первого раза => потеряется часть информации и принимающая система упадёт): http://habrahabr.ru/post/213063/

Жека 17.11.2015 05:43

Ответ: С++ Sockets
 
Почему TCP не гарантирует доставку? Должен.
Я полагаю, при загруженной сети может дойти кусок, и когда сеть просрётся, то дойдёт оставшаяся часть.
Поэтому парсить сразу каждый принятый кусок не нужно, нужно накапливать данные в буфере до тех пор, пока не встретим признак_окончания_команды. Для текстового формата этим признаком может служить \r\n .

impersonalis 17.11.2015 12:31

Ответ: С++ Sockets
 
Цитата:

Сообщение от Жека (Сообщение 301562)
Почему TCP не гарантирует доставку?

читай внимательнее:
Цитата:

Сообщение от impersonalis (Сообщение 301502)
при асинхронном сокете tcp (и, надо полагать, udp - не работал с ним)

и да - он гарантированно доставит то, что он отметил (путём указания длины в возвращаемом значении) как отправленное. Синхронный заблокирует вызывающий поток до конца отправки всего буфера. Для большого количества приложений это очень неудобно.
Суть такова: ты хочешь отправить команду "12345". Но в момент отправки сеть едва скрипит, поэтому за малое время, выделенное на отправку, уходит только "123", и функция возвращает тебе число 3, как индикатор количества отправленных байт. Очень часто, это число никто не проверяет (ограничиваясь только проверкой на отсутствие сбоя), считая что асинхронный сокет будет сам как-то выкручиваться и отправлять оставшееся "45". НЕТ. При следующем вызове надо будет отправить* "45" ещё раз. И опять убедиться - что отправилось.

Про контроль прочитанного мы знаем ещё по блитцу. В общем-то никто не гарантирует какими порциями информация будет уходить по сети. Как правило, происходит некая аккумуляция данных, чтобы не забивать сетку маленькими пакетами. Но при необходимости, это можно изменить. Но это отдельная большая тема.
Я бы не советовал в общем случае привязываться к разделителю строк. Сепаратор надо выбирать исходя из передаваемого контента. Возможно, его получится сделать более коротким, или наоборот - проще сделать подлиннее (например, исходя из больших размеров "строки").


Часовой пояс GMT +4, время: 21:42.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot