Eee, ten kod tylko strasznie wygląda (dużo gwiazdek), ale w rzeczywistości jest banalnie prosty. Obkomentuję go trochę i dodam przykładową detekcję endianess (Uwaga! Pod Windows ta detekcja może nie działać... nie wiem jak tam się takie rzeczy robi).
#include <endian.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
/* Tu sobie wykrywamy na czym biegamy i odpowiednio
definiujemy makro CRLF_AS_16BIT_INT */
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define CRLF_AS_16BIT_INT 2573
#else
#define CRLF_AS_16BIT_INT 3338
#endif
int main(void)
{
char *pos, packet[17], buf[64];
/* Żeby nie operować na cstringu (bo z recv() dostaniesz bajty)
symulujemy tablicę bajtów ze stringa. Na końcu nie będzie
bajtu zerowego. W tablicy wskazywanej przez `packet' będą
dane niby odebrane z sieci. Celem jest wyciągnięcie bajtów
przed '\r\n' */
memcpy(packet, "Foo bar baz\r\nFrob", 17);
/* `pos' wskazuje teraz na pierwszy bajt tablicy `packet'. Ponieważ
będziemy zmieniać offset, nie możemy użyć `packet' do iteracji */
pos = packet;
/* Clou algorytmu. "(uint16_t*)pos" to ściema dla kompilatora.
Wciskamy mu ciemnotę, że dwa kolejne bajty (poczynając od
wskazywanego przez `pos') w pamięci to tak naprawdę jest wskaźnik
na 16bitowy integer bez znaku. Na architekturze Little Endian
(np. Intel) bajty '\r\n' (0x0D i 0x0A) dają 0x0D0A, co jako 16bitowy
int bez znaku daje wartość 2573 (decymalnie), której będziemy szukać.
*() to zwykła dereferencja naszego ściemnianego wskaźnika na int.
Ta "sztuczka" zadziała tylko platformach, na których char ma 8 bitów.
W warunku pętli powinien jeszcze być test zapobiegający wyjechaniu
poza tablicę. Z pętli musimy wyskoczyć kiedy `pos' osiągnie wartość
o 2 mniejszą od wielkości przeszukiwanej tablicy (dwa bajty: '\r\n').
Z tym już sobie poradzisz */
while ( *(uint16_t*)pos != CRLF_AS_16BIT_INT )
/* Jeśli dwa bajty wskazywane przez `pos' nie mają szukanej wartości
przeskakujemy o jeden bajt dalej i powtarzamy zabawę. Zwiększamy
wartość !wskaźnika! (arytmetyka na wskaźnikach to fajna rzecz) */
pos++;
/* Pominąłem sprawdzenie czy '\r\n' zostało znalezione i zakładamy tutaj,
że znaleźliśmy. Kopiujemy zatem do bufora docelowego dane z tablicy
`packet', zaczynając od jej początku. Kopiujemy tyle bajtów, ile przelecieliśmy
(ponownie arytmetyka na wskaźnikach) */
memcpy(buf, packet, pos - packet);
/* Doklejamy bajt zerowy, robiąc ze skopiowanych danych pełnoprawnego
c-stringa. Pamiętaj, że `pos - packet - 1' nie może być większe niż rozmiar
docelowego bufora, gdyż zaczniesz bazgrać po sąsiednich danych w pamięci.
Najlepiej bufor alokować dynamicznie, o wielkości odpowiedniej do rozmiaru
kopiowanych danych (pos - packet) + 1 bajt na terminator (bajt zerowy) */
buf[pos - packet] = 0;
/* Do celów demonstracyjnych printujemy dane. Będą one pozbawione '\r\n',
więc w formacie dodajemy newline */
printf("%s\n", buf);
return 0;
}
Nic nadzwyczajnego, jak widać. Trzeba jeszcze obsłużyć podzielony separator, gdyż z jednego recv() możesz dostać "<pakiet>\r", a z drugiego "\n<inny pakiet="pakiet">". Powyższy "Tour de Tablica" sprawdza po dwa bajty, więc taki przypadek nie zostanie wykryty. Ja to robię tak, że w buforze dla recv() zawsze po przeskanowaniu kopiuję ostatni bajt na początek jeśli nie ma już miejsca w buforze (jeśli jest, to używam go do doczytania reszty) i do recv() przekazuję wskaźnik na drugi bajt (i ilość danych do odebrania ustawiam na wielkość bufora - 1 of course). To najbanalniejszy sposób.