как бы вы поместили второе сообщение обратно в буфер recv (), как только вы поняли, что в вашем буфере больше данных, чем нужно?
Просто не кладитевторое сообщение из буфера recv (), я вижу для этого два пути:
1) сделать первое
ssize_t size = recv(sockfd, buf, len, MSG_PEEK | MSG_TRUNC);
- MSG_TRUNC (только AF_PACKET) дает вам реальный размериз доступных данных, длина которых не может быть усечена до len
- с MSG_PEEK, полученные данные не удаляются из очереди.
Это позволяет вам анализировать данные просмотра и
- , если оно является частью первого сообщения, но не его концом, вы читаете (не просматриваете) его
recv(sockfd, buf, size);
затем вы переделываете предыдущее recv и т. д. - , если у вас есть (конец) первого сообщения и вы можете быть частью второго сообщения, которое вы знаете subSize , которое нужно прочитать и сделать
recv(sockfd, buf, subSize);
, и ваше второе сообщение все еще доступно для следующего revc
Конечно, каждый раз, когда вы читаете часть первого сообщения, указатель buf прогресс, чтобы не переписывать уже прочитанную часть.
Используйте malloc
затем realloc
, чтобы увеличить размер буфера, получающего первое сообщение
2), очень распространенный способ егоотправьте размер сообщения перед самим сообщением, что позволит получателю сначала прочитать размер, а затем прочитать данные в цикле, пока все сообщение не будет прочитано.Для совместимости с прямым / младшим порядковым номером, если сообщение больше 255 байтов, используйте htons / htonl / ntohs / ntohl для размера
Я не знаю, что произойдетхотя к данным, которые не помещаются в буфер.
Если вы говорите о записи из буфера, никто не знает, потому что поведение не определено, если у вас есть шанс, что у вас есть ошибка сегментации, которая виднавопреки драматическому повреждению памяти, последствия которого могут быть видны очень поздно.Но, как вы можете видеть в двух вышеупомянутых решениях, к счастью, этого не происходит
Пример для первого случая, использующего TCP / IP (тогда не MSG_TRUNC
), пробел указывает конец каждогобуфер (но я не читаю символ за символом, чтобы быть совместимым с более сложным определением конца буфера).
Сервер получает в аргумент строки для отправки, каждый аргумент отправляется в одном отправьте независимо от того, содержит он пробелы или нет, последний символ последнего аргумента должен быть пробелом.
Клиент получает один аргумент размером, который нужно (попытаться) прочитатькаждый раз он печатает каждый буфер 'peek' (для отладки) и каждый буфер.
server.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
int main(int argc, char ** argv)
{
errno = 0;
int ssock = socket(AF_INET, SOCK_STREAM, 0);
if (ssock == -1) {
perror("socket()");
return -1;
}
int reuse = 1;
if (setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) {
perror("setsockopt() SO_REUSEADDR)");
return -1;
}
#ifdef SO_REUSEPORT
if (setsockopt(ssock, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) == -1) {
perror("setsockopt() SO_REUSEPORT)");
return -1;
}
#endif
struct sockaddr_in ssin = { 0 };
ssin.sin_addr.s_addr = htonl(INADDR_ANY);
ssin.sin_port = htons(1024);
ssin.sin_family = AF_INET;
if(bind (ssock, (struct sockaddr*) &ssin, sizeof(ssin)) == -1)
{
perror("bind()");
return -1;
}
if(listen(ssock, 1) == -1)
{
perror("listen()");
return -1;
}
struct sockaddr_in csin = { 0 };
socklen_t csz = sizeof(csin);
int csock = accept(ssock, (struct sockaddr*) &csin, &csz);
if (csock == -1) {
perror("accept()");
return -1;
}
for (int i = 1; i < argc; ++i) {
if (send(csock, argv[i], strlen(argv[i]), 0) == -1) {
char s[32];
sprintf(s, "send %i", i);
perror(s);
}
}
close(csock);
close(ssock);
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>
int main(int argc, char ** argv)
{
if (argc != 2) {
printf("Usage : %s <length>\n", *argv);
return 0;
}
int len;
char c;
if ((sscanf(argv[1], "%d%c", &len, &c) != 1) && (len < 1)) {
fprintf(stderr, "invalid length\n");
return -1;
}
errno = 0;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
perror("socket()");
return -1;
}
struct sockaddr_in sin = { 0 };
sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
sin.sin_port = htons(1024);
sin.sin_family = AF_INET;
if(connect (sock, (struct sockaddr*) &sin, sizeof(sin)) == -1)
{
perror("connect()");
return -1;
}
for (;;) {
size_t ln = len;
char * buf = malloc(ln + 1);
if (buf == NULL) {
fprintf(stderr, "cannot malloc");
break;
}
size_t off = 0;
for (;;) {
ssize_t sz = recv(sock, buf + off, len, MSG_PEEK); /* no MSG_TRUNC : AF_INET */
if (sz <= 0) {
free(buf);
close(sock);
return -1;
}
buf[off + sz] = 0;
/* debug */
printf("peek '%s'\n", buf + off);
char * p = strchr(buf + off, ' ');
if (p != NULL) {
recv(sock, buf + off, p - buf - off + 1, 0);
*p = 0;
printf("full buff is '%s'\n", buf);
free(buf);
break;
}
recv(sock, buf + off, sz, 0);
off += sz;
ln += sz;
buf = realloc(buf, ln + 1);
if (buf == NULL) {
fprintf(stderr, "cannot malloc");
break;
}
}
}
close(sock);
}
Компиляция и выполнение:
pi@raspberrypi:~ $ gcc -pedantic -Wextra server.c -o se
pi@raspberrypi:~ $ gcc -g -pedantic -Wextra client.c -o cl
pi@raspberrypi:~ $ ./se "123 456 78901234567" "8 1 " &
[1] 11551
pi@raspberrypi:~ $ ./cl 5
peek '123 4'
full buff is '123'
peek '456 7'
full buff is '456'
peek '78901'
peek '23456'
peek '78 1 '
full buff is '789012345678'
peek '1 '
full buff is '1'
[1]+ Fini ./se "123 456 78901234567" "8 1 "
pi@raspberrypi:~ $
Выполнения под valgrind (в отдельных терминалах):
pi@raspberrypi:~ $ valgrind ./se "123 456 78901234567" "8 1 "
==11602== Memcheck, a memory error detector
==11602== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==11602== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==11602== Command: ./se 123\ 456\ 78901234567 8\ 1\
==11602==
==11602==
==11602== HEAP SUMMARY:
==11602== in use at exit: 0 bytes in 0 blocks
==11602== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==11602==
==11602== All heap blocks were freed -- no leaks are possible
==11602==
==11602== For counts of detected and suppressed errors, rerun with: -v
==11602== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)
pi@raspberrypi:~ $ valgrind ./cl 5
==11604== Memcheck, a memory error detector
==11604== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==11604== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==11604== Command: ./cl 5
==11604==
peek '123 4'
full buff is '123'
peek '456 7'
full buff is '456'
peek '78901'
peek '23456'
peek '78 1 '
full buff is '789012345678'
peek '1 '
full buff is '1'
==11604==
==11604== HEAP SUMMARY:
==11604== in use at exit: 0 bytes in 0 blocks
==11604== total heap usage: 8 allocs, 8 frees, 1,081 bytes allocated
==11604==
==11604== All heap blocks were freed -- no leaks are possible
==11604==
==11604== For counts of detected and suppressed errors, rerun with: -v
==11604== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)