Попробуйте закомментировать настройку тайм-аута.Я никогда не использую это со своей стороны и не испытываю проблемы, о которой вы говорите.
// Create and set timeout
struct timeval timeout_chars;
timeout_chars.tv_sec = TIMEOUT_SECS;
timeout_chars.tv_usec = TIMEOUT_USECS;
setsockopt(socket_conn, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_chars, sizeof(timeout_chars));
Чтобы избежать блокировки, вы можете настроить сокет как неблокируемый сокет, а затем использовать select()
или poll()
, чтобы получить больше данных.Обе эти функции могут использовать время ожидания, как представлено выше.Однако с неблокирующим сокетом вы должны убедиться, что чтение работает как положено.Во многих случаях вы получите частичное чтение и вам придется ждать (select()
или poll()
) еще раз для получения дополнительных данных.Так что код будет немного сложнее.
socket_conn = socket(COMM_DOMAIN, SOCK_STREAM | SOCK_NONBLOCK, 0);
Если безопасность является потенциальной проблемой, я бы также установил SOCK_CLOEXEC
, чтобы запретить дочернему процессу доступ к тому же сокету.
std::vector<struct pollfd> fds;
struct pollfd fd;
fd.fd = socket_conn;
fd.events = POLLIN | POLLPRI | POLLRDHUP; // also POLLOUT for writing
fd.revents = 0; // probably useless... (kernel should clear those)
fds.push_back(fd);
int64_t timeout_chars = TIMEOUT_SECS * 1000 + TIMEOUT_USECS / 1000;
int const r = poll(&fds[0], fds.size(), timeout_chars);
if(r < 0) { ...handle error(s)... }
Другой метод, предполагающий, что размер заголовка четко определен и никогда не изменяется, состоит в том, чтобы прочитать заголовок, а затем использовать информацию заголовка для чтения остальных данных.В этом случае вы можете оставить блокирующий сокет без тайм-аута.Из ваших структур я понятия не имею, что это может быть.Итак ... давайте сначала определим такую структуру:
struct header
{
char sync[4]; // four bytes indicated a synchronization point
uint32_t size; // size of packet
... // some other info
};
Я поставил поле "sync".В TCP часто люди добавляют такое поле, поэтому, если вы потеряете позицию, вы можете перейти к следующей синхронизации, считывая по одному байту за раз.Честно говоря, с TCP вы никогда не получите такую ошибку передачи.Вы можете потерять соединение, но никогда не потеряете данные из потока (т. Е. TCP походит на идеальный FIFO в вашей сети.) При этом, если вы работаете с критически важным программным обеспечением, синхронизация, а также контрольная сумма будет очень кстати.
Далее мы read()
просто заголовок.Теперь мы знаем точный размер этого пакета, поэтому мы можем использовать этот конкретный размер и считывать ровно столько байтов в нашем буфере пакетов:
struct header hdr;
read(socket_conn, &hdr, sizeof(hdr));
read(socket_conn, packet, hdr.size /* - sizeof(hdr) */);
Очевидно, read()
может вернуть ошибку и размерв заголовке может быть задан байтовый порядок байтов (поэтому вам нужно поменять местами байты на процессорах x86)Но это должно помочь вам.
Кроме того, если размер, найденный в заголовке, включает количество байтов в заголовке, обязательно вычтите это количество при чтении остальной части пакета.
Также неверно следующее:
memset(&socket_server_address, '0', sizeof(socket_server_address));
Вы хотели очистить структуру нулями, а не символом ноль.Хотя, если он соединяется, это значит, что это не имеет большого значения.Просто используйте 0
вместо '0'
.