Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Сети > Непонятки с приемом у сокета


Автор: Athlon 14.6.2006, 12:38
Есть у меня две программы которые общаются строками через сокеты. Для этого создал два класса производных от CSocket и отправляю стороки с помощью Send(), а в OnReceive() принимаю. Вроде бы  проще некуда, но когда начал тестировать обнаружил интересный баг - клиент иногда принимает несколько строк как одну.
Что бы это могло быть и как с этим явлением бороться  smile  ????? 

Автор: Romikgy 14.6.2006, 13:35
Имхо буфер сокета набирает данные и когда он (буфер) переполняется он его выпихивает в сокет (в сеть) , в твоем случае делать принудительное выталкивание данных, что то типа как для файла flush или делать задержки между отправкой сенд (таймауты),
и второй вариант забить на все это, и вставлять в строке, символ конца строки (свой допустим "+") , а при выводе на экран заменять этот символ на перевод строки 

Автор: Athlon 14.6.2006, 13:46
Romikgy, вариант с разделителем строки конечно интересный, но ты лучше подскажи как сделать
Цитата
 принудительное выталкивание данных
 

Автор: Romikgy 14.6.2006, 14:07
Цитата(Athlon @  14.6.2006,  11:38 Найти цитируемый пост)
CSocket

Чье это счастье , что за среда , VC? 
хелпнадо смотреть найду его подскажу чтото  

Автор: Athlon 17.6.2006, 16:39
После утомительных поисков нашел выход - отключить алгоритм Нейгла (который по возможности объединяет мелкие сообщения до размера сегмента). Отключаю его следующим способом:
Код

bool opt = true;
int res = s_sock[*index]->SetSockOpt(TCP_NODELAY, &opt, sizeof(bool), IPPROTO_TCP);
if(res == 0)
{
//обработка ошибки
}

Но все равно сообщения которые я отправляю клиенту в цикле, например так:
Код

while(log->not_empty())
{
    CString* comment = log->get_head();
    comment->Insert(0,"c");
    s_sock[index]->Send(*comment, comment->GetLength());
    delete comment;
}

все также с приходят то по отдельности то вместе. Может есть еще вариант заставить сокет отправлять сообщения немедленно или я что-то делаю неправильно?

P.S. Romikgy, речь идет о CSocket из MFC, а среда разработки MS VC 6.0  

Автор: Romikgy 19.6.2006, 14:42
Можно еще посоветовать
SO_SNDBUF    поставить в 1
и с SO_SNDTIMEO    поигратся
Цитата(Athlon @  17.6.2006,  15:39 Найти цитируемый пост)
речь идет о CSocket из MFC, а среда разработки MS VC 6.0  

У меня этого нет так что ничего не подскажу, да и лично я не люблю VC и его MFC имхо тупой он.
+Имхо лучше сделать с разделителями строк!

да еще можешь попробывать ставить задержки
Цитата(Athlon @  17.6.2006,  15:39 Найти цитируемый пост)
s_sock[index]->Send(*comment, comment->GetLength());

после этого , допустим на 10 мсек, может поможет 

Автор: ptr 19.6.2006, 14:52
Athlon выложи код приёма, отправки сообщений. 

Автор: Athlon 19.6.2006, 15:31
Я в принципе уже решил вопрос вводом разделителя строки, и почитав умную книгу понял что это самый правильный способ (http://www.syt.edu.severodvinsk.ru/znai/megabook/internet/tcp-ip/gl3/gl3.html - совет 15). Но если интересно могу выложить.
Серверный сокет наследуется от CSocket и отправляет строковые сообщения функцией предка Send
Код

//s_sock[index] - указатель на сокет сервера
while(log->not_empty())    
{    
    CString* comment = log->get_head();    
    comment->Insert(0,"c");
    comment->Insert(comment->GetLength(), limiter);    
    s_sock[index]->Send(*comment, comment->GetLength());    
    delete comment;    
}

а клиент принимает сообщения в обработчике OnReceive - вот код клиентского сокета:
Код

class CClientDlg;

class CClientSocket : public CSocket  
{
protected:
    CClientDlg* pdlg;  //диалоговое окно используется для вывода
public:
    CClientSocket(CClientDlg* m_dlg);
    virtual ~CClientSocket();
    virtual void OnClose(int nErrorCode);
    virtual void OnReceive(int nErrorCode);
};

void CClientSocket::OnReceive(int nErrorCode) 
{
    char buffer[4096];
    int nRead = Receive(buffer,4096);
    if(nRead == SOCKET_ERROR)
    {
        AfxMessageBox("ClientSocket :: OnReceive ERROR !!!");
        return;
    }
    buffer[nRead] = '\0';
    CString message = buffer;
    while(message != "")
    {
        //отделяю префикс от сообщения
        char msg_type = message[0];        
        int index = message.Find(limiter);
        if(index == -1)
        {
            message = buffer;
            pdlg->receive_question(message);
            break;
        }
        CString fragment = message.Left(index);
        fragment.Delete(0, 1);
        message.Delete(0, index+1);
        switch(msg_type)
        {
            case 'l':
            {
                pdlg->show_base_list(fragment);
                break;
            }
            .......
            case 'c':
            {
                pdlg->receive_comment(fragment);
                break;
            }
            default:
            {
                pdlg->receive_question(fragment);
            }
        }
    }
    pdlg->scroll_list();
    CSocket::OnReceive(nErrorCode);
}

в начальной версии не было цикла while для разбивки на строки по разделителю.

P.S. Только не над лекций о том что нужно всеми правдами и неправдами избегать switch.  smile  

Автор: Romikgy 19.6.2006, 16:04
И че не работает так как надо ? 

Автор: Artemon 7.7.2006, 18:10
Чего следует избегать, так это посылки строк.
Самый надежный вариант посылать буфер.
 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)