Üzenetet szeretnék küldeni a Binder osztályomnak a C nyelvű TCP socket kapcsolaton keresztül. Ezen a kapcsolaton át kell adnom egy kéréstípust (char*), ip címet (int), argTypes(int array) stb. ) módszerrel. Mi a legjobb módszer az összes információ egyetlen üzenetben történő elküldésére?
Különböző típusú üzenetek írása a C aljzatain keresztül
- Mivel próbálkoztál, és hogyan sikerült? 01.07.2012
Válaszok:
Nincs garancia arra, hogy egyetlen olvasási/írási művelettel el tudja küldeni/fogadni az összes adatot; túl sok tényező befolyásolhatja a minőséget/packet-size/connection-stability/etc.
Ez a kérdés/válasz ezt magyarázza meg.
Néhány C-példa itt.
Jó magyarázata a socket programozásáról C-ben.
A a TCP/IP gyors áttekintése.
A különböző típusú üzenetek küldése:
Az Ön által küldött adatokat a szerver-alkalmazásból kapja meg az ügyfélalkalmazás, amely aztán tetszés szerint értelmezheti ezeket az adatokat.
Ha az adatok kapcsolódnak, létrehozhat egy struktúrát egy külön fejlécben, és használhatja mind a kliens, mind a kiszolgáló kódjában, és elküldheti ennek a struktúrának a változóját. Ha nem kapcsolódik egymáshoz, akkor nem tudom, miért kell egyetlen üzenetként elküldeni őket.
Ha egyetlen írással és egyszeri olvasással szeretné továbbítani és fogadni adatait, akkor datagram-foglalatot kell használnia. Mivel a datagram socket kapcsolat nélküli, nem használhatja a write
/read
. Ehelyett használja a sendto
/recvfrom
vagy sendmsg
/recvmsg
. Mivel a datagram socket megbízhatatlan, saját protokollt kell megvalósítania, hogy elviselje a renden kívüli adattovábbítást és adatvesztést.
Ha nem akar foglalkozni a datagram socket megbízhatatlan természetével, akkor szeretne egy stream socketet. Mivel stream aljzat van csatlakoztatva, a továbbított adatok garantáltak és rendben vannak. Ha az elküldött adatok mindig azonos méretűek, akkor lemásolhat egy datagramot úgy, hogy blokkoló módot használ a send
híváshoz, majd átadja a MSG_WAITALL
recv
hívást.
#define MY_MSG_SIZE 9876
int my_msg_send (int sock, const void *msg) {
int r, sent = 0;
do {
r = send(sock, (const char *)msg + sent, MY_MSG_SIZE - sent,
MSG_NOSIGNAL);
if (r <= 0) {
if (r < 0 && errno == EINTR) continue;
break;
}
sent += r;
} while (sent < MY_MSG_SIZE);
if (sent) return sent;
return r;
}
int my_msg_recv (int sock, void *msg) {
int r, rcvd = 0;
do {
r = recv(sock, (char *)msg + rcvd, MY_MSG_SIZE - rcvd, MSG_WAITALL);
if (r <= 0) {
if (r < 0 && errno == EINTR) continue;
break;
}
rcvd += r;
while (rcvd < MY_MSG_SIZE);
if (rcvd) return rcvd;
return r;
}
Figyelje meg, hogy a szoftvernek még mindig meg kell küzdenie bizonyos hibaesetekkel. EINTR
esetén az I/O műveletet újra meg kell próbálni. Egyéb hibák esetén előfordulhat, hogy az adatok kézbesítése vagy visszakeresése nem teljes. Általában azonban a socketek blokkolásához csak egy iterációt várunk a fenti do
-while
hurokhoz.
Ha üzenetei nem mindig azonos méretűek, akkor szüksége van egy módra az üzenetek keretezésére. A keretezett üzenet azt jelenti, hogy módra van szüksége az üzenet kezdetének és az üzenet végének észlelésére. Talán a legegyszerűbb módja annak, hogy egy üzenetet a streaming socket felett keretezzünk, ha az üzenet méretét megelőzzük. Ezután a vevő először a méretet olvassa fel, majd elolvassa az üzenet többi részét. Ehhez könnyen adaptálnia kell a my_msg_send
és my_msg_recv
mintakódját.
Végül ott van magának az üzenetnek a kérdése. Ha az üzenetek nem mindig azonos méretűek, ez valószínűleg azt jelenti, hogy egy vagy több változó hosszúságú rekord van az üzenetben. Ilyen például egy értéktömb vagy egy karakterlánc. Ha a feladó és a fogadó is egyetért a rekordok sorrendjében, akkor elegendő minden változó hosszúságú rekordot megelőzni annak hosszával. Tehát tegyük fel, hogy az üzenet felépítése a következő:
struct my_data {
const char *name;
int address;
int *types;
int number_of_types;
};
Ezután a struct my_data
példányát így képviselheti:
NAME_LEN : 4 bytes
NAME : NAME_LEN bytes
ADDRESS : 4 bytes
TYPES_LEN : 4 bytes
TYPES : TYPES_LEN * 4 bytes
A NAME_LEN
a strlen(msg->name)
-ból, a TYPES_LEN
pedig a msg->number_of_types
-ből kapná az értékét. Amikor elküldi az üzenetet, azt a fenti üzenet teljes hossza előzi meg.
32 bites mennyiségeket használtunk a hossz ábrázolására, ami valószínűleg elegendő az Ön céljaihoz. Ha egy számot aljzaton keresztül továbbítunk, a feladónak és a fogadónak meg kell állapodnia a szám bájtsorrendjében. Ez azt jelenti, hogy az 1-es szám 0.0.0.1
vagy 1.0.0.0
. Ez általában a hálózati bájtrendezés segítségével kezelhető, amely az előbbit használja. A socket fejlécfájlok a htonl
makrót tartalmazzák, amely a 32 bites értéket a gazdagép natív bájtsorrendjéből hálózati bájtsorrendgé alakítja. Ez akkor használatos, ha értéket tárol, például NAME_LEN
. A vevő a megfelelő ntohl
makróval visszaállítja az átvitt értéket a gazdagép által használt reprezentációra. A makrók természetesen nem lehetnek műveletek, ha a gazdagép natív bájtsorrendje már megegyezik a hálózati bájtsorrenddel. Ezeknek a makróknak a használata különösen fontos, ha heterogén környezetben küldünk és fogadunk adatokat, mivel a küldő és a fogadó eltérő állomás-byte-sorrenddel rendelkezhet.