сокеты потоков домена unix, отправляющие больше данных, чем должно быть - PullRequest
1 голос
/ 21 мая 2009

У меня есть две простые программы, которые обмениваются данными через сокет домена unix. Одна программа считывает данные из очереди и отправляет их другому приложению. Перед отправкой каждый фрагмент данных добавляется спереди четырьмя байтами длиной, если он меньше четырех байтов, оставшиеся байты являются символом '^'.

Затем клиентское приложение считывает первые четыре байта, устанавливает буфер в соответствующий размер и затем читает остальные. Проблема, которая у меня возникает, заключается в том, что в первый раз через сообщение будет отправлено отлично. Каждое другое время после этого отправляются дополнительные данные, поэтому выдается сообщение типа «какой хороший день», как «какой хороший день ?? X ??». Поэтому я чувствую, что буфер очищается неправильно, но я не могу его найти.

Код клиента:

listen(sock, 5);
for (;;) 
{
    msgsock = accept(sock, 0, 0);
    if (msgsock == -1)
        perror("accept");
    else do 
    {
        char buf[4];
        bzero(buf, sizeof(buf));
        if ((rval = read(msgsock, buf, 4)) < 0)
        perror("reading stream message");

        printf("--!%s\n", buf);

        string temp = buf;
        int pos = temp.find("^");
        if(pos != string::npos)
        {
            temp = temp.substr(0, pos);
        }

        int sizeOfString = atoi(temp.c_str());
        cout << "TEMP STRING: " << temp << endl;
        cout << "LENGTH " << sizeOfString << endl;
        char feedWord[sizeOfString];
        bzero(feedWord, sizeof(feedWord));

        if ((rval = read(msgsock, feedWord, sizeOfString)) < 0)
              perror("reading stream message");

          else if (rval == 0)
              printf("Ending connection\n");
          else
              printf("-->%s\n", feedWord);
              bzero(feedWord, sizeof(feedWord));
              sizeOfString = 0;
              temp.clear();
      } 
        while (rval > 0);
      close(msgsock);
  }
  close(sock);
  unlink(NAME);

Код сервера

                pthread_mutex_lock(&mylock);
                string s;
                s.clear();
                s = dataQueue.front();
                dataQueue.pop();
                pthread_mutex_unlock(&mylock);

                int sizeOfString = strlen(s.c_str());
                char sizeofStringBuffer[10];

                sprintf(sizeofStringBuffer, "%i", sizeOfString);
                string actualString = sizeofStringBuffer;
                int tempSize = strlen(sizeofStringBuffer);

                int remainder = 4 - tempSize;
                int x;
                for(x =0; x < remainder; x++)
                {
                    actualString = actualString + "^";
                }

                cout << "LENGTH OF ACTUAL STRING: " << sizeOfString << endl;

                actualString = actualString + s;

                cout << "************************" << actualString << endl;
                int length = strlen(actualString.c_str());

                char finalString[length];
                bzero(finalString, sizeof(finalString));
                strcpy(finalString, actualString.c_str());

                           if (write(sock, finalString, length) < 0)
                           perror("writing on stream socket");      

Ответы [ 3 ]

2 голосов
/ 21 мая 2009

Вы должны проверять возвращаемые значения как write, так и read не только для -1, но и для коротких (меньше запрашиваемых) операций записи / чтения. Вы также, похоже, просто продолжаете после печати ошибки с perror - выполните exit(2) или что-то там.

2 голосов
/ 21 мая 2009

Вместо того, чтобы дополнять длину вашего пакета '^', вам будет гораздо лучше просто сделать:

snprintf(sizeofStringBuffer, 5, "%04d", sizeOfString);

так, чтобы значение было дополнено 0 - тогда вам не нужно разбирать символы '^' в коде получателя.

Пожалуйста, также отредактируйте ваш код отладки - в текущем коде есть только один write(), и он не соответствует вашему описанию протокола.

В идеале - разделить вашу процедуру отправки на собственную функцию. Вы также можете воспользоваться преимуществом writev() для обработки объединения строки, содержащей поле «длина», с буфером, содержащим фактические данные, и последующей отправкой их в виде одного атомарного элемента write().

Следующий непроверенный код:

int write_message(int s, std::string msg)
{
     struct iovec iov[2];
     char hdr[5];

     char *cmsg = msg.c_str();
     int len = msg.length();

     snprintf(hdr, 5, "%04d", len);  // nb: assumes len <= 9999;

     iov[0].iov_base = hdr;
     iov[0].iov_len = 4;

     iov[1].iov_base = cmsg;
     iov[1].iov_len = len;

     return writev(s, iov, 2);
}
0 голосов
/ 23 мая 2009

Две вещи:

Первый - на стороне сервера вы записываете конец массива.

char finalString[length];
bzero(finalString, sizeof(finalString));
strcpy(finalString, actualString.c_str());

strcpy() скопирует length+1 символов в finalString (символ вытягивает нулевой терминатор).

Второй (и, скорее всего, проблема) - на стороне клиента вы не завершаете строку, в которой вы читаете, значение NULL, поэтому printf() напечатает вашу строку, а затем все, что находится в стеке, до точки это ноль.

Увеличьте оба буфера на один, и вы должны быть в лучшей форме.

...