[C] Socket programmeren: msg voor msg ontvangen bij TCP/IP?

Pagina: 1
Acties:
  • 129 views sinds 30-01-2008
  • Reageer

  • Ronald Koeman
  • Registratie: December 2001
  • Laatst online: 07-05 19:52
Ik wil TCP/IP communicatie opzetten, maar het lukt me niet om socket messages één voor één uit te lezen. Bij UDP lukte dat prima. De code van de server:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>

#define buffermax 250
#define messagemax 16

#pragma comment(lib, "ws2_32.lib") // to get rid of linker errors
char EOT=0x04;
char kansloos;

int iMode;

int count=0;
int ret_chars,j,i=0;
int client_sock;
int n=0;
int error=0;
int Errorcode;
char buff[buffermax];
char number[10];

int huidig;
int laatste=1;
int teller=0;
int aantal;

int tcp_sock;
int bindok;

struct sockaddr_in data;

/* Van Gabriel ivm gebruik ws2_32.dll */
int err ;
WORD wVersionRequested = MAKEWORD( 2, 2 );

WSADATA wsaData;

int main(int argc, char *argv[])
{

/* invoer aantal verzonden pakketten */

 /* Initialize ws2_32.dll */
  printf("\nInitializing ws2_32.dll ...\n");
   err = WSAStartup( wVersionRequested, &wsaData );

  if (err != 0) 
  {
   /* Could not find usable WinSock.DLL */
   printf ("\nerr: %d\n", err);
   exit(1);
  }
//ready to open socket
  printf("press any key to continue\nwaiting");
  getch();
  printf("\n");

//Open Socket
  tcp_sock = socket( AF_INET, SOCK_STREAM, 0);
  
  if(tcp_sock < 0)
  {
        printf("Could not open socket %d\n\r", tcp_sock);
        getch();
        return  tcp_sock;
  }
        
//Structure klaarzetten
  if(tcp_sock)
  {
        memset( &data, 0, sizeof( data ) );   //  clear out our structure
        data.sin_family = AF_INET;            //  Set Internet address Family
        data.sin_addr.s_addr = htonl( NULL ); //  Client address will be filled 
                                              //  in automatically
        data.sin_port = htons(1010);        // This sets the TCP port number 
  }

//bind 
  bindok = bind( tcp_sock, ( struct sockaddr *)&data, sizeof( data  ) ); //  call to bind 
    if( bindok < 0 )
    {
        printf("could not bind socket to file descriptor\n\r");
    }
//client connects
//accept
    listen( tcp_sock, 10);        //  listen for 10 clients Max
    tcp_sock = accept( tcp_sock, NULL, NULL );        //  call to accept
//receive
    memset( buff, 0, sizeof( buff ) );
while (1)
{  
    ret_chars = recv( tcp_sock, buff, sizeof(buff), 0);
//  printf("%s\n", buff);

    if( ret_chars > 0 )
    {
                //verwerk data
                //code voor de leesbaarheid weggelaten
                //ik ben gedwongen om hier met een seperatieteken te werken om messages te               
                //kunnen onderscheiden!
                }
    else if( ret_chars < 0 )
    {
        printf("error recv'ing data\n");
        Errorcode=WSAGetLastError();

        if (Errorcode==10054) 
        {
          printf("Connection failed, client down\n"); 
          break;
        }
        else  printf("Errorcode = %d\n", Errorcode);

    }
    else if( ret_chars == 0 )
    {
        printf("client session terminated\n");
        break;
    }


    memset( buff, 0, sizeof( buff ) );

}

  printf("%d packages received\n", aantal);

  printf("press any key to continue\n");
  getch();

//close socket
closesocket(tcp_sock);

}

Als ik bij UDP de getallen 1-10 in 10 berichten ontvang kan ik die getallen "los" uitlezen. Bij TCP krijg ontvang ik eerst netjes "1" waarna ik bij de tweede keer uitlezen "2345678910" in mijn ontvangstbuffer lees. Een oplossing die ik zelf had bedacht is een separatieteken in te voeren bij verzending, waardoor ik messages alsnog kan onderscheiden,maar dat wil ik natuurlijk eigenlijk niet. Iemand een suggestie?

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Sockets zijn stream-based en niet message-based. Een stream heeft per definitie geen separatie. "1" "2" is precies hetzelfde als "12".

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 04:30

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je zou ipv een seperator ook kunnen aangeven hoeveel bytes deze "message" in beslag gaat nemen. Dan weet je ook vantevoren hoeveel je uit moet lezen en dus op hoeveel verzonden bytes je moet wachten, ipv dat je de stream moet gaan scannen naar je seperator.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 07-05 10:04
Ronald Koeman schreef op vrijdag 01 april 2005 @ 09:51:
Een oplossing die ik zelf had bedacht is een separatieteken in te voeren bij verzending, waardoor ik messages alsnog kan onderscheiden,maar dat wil ik natuurlijk eigenlijk niet. Iemand een suggestie?
Je zult zelf iets van een framestructuur moeten bouwen. Je eigen protocol bovenop TCP zogezegd.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
Of direct het reliable datagram protocol (RDP) gebruiken.

  • Ronald Koeman
  • Registratie: December 2001
  • Laatst online: 07-05 19:52
Bedankt voor jullie reacties.

Ik heb na veel gewik en geweeg toch maar besloten om het bij UDP communicatie te houden, gezien de research die ik nog moet doen op het gebied van TCP en de tijd die ik nog beschikbaar heb voor m'n projectje :)

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Vergeet niet dat UDP unreliable is. Packets hoeven niet aan te komen, en kunnen zelfs in de verkeerde volgorde aankomen. als je dus een message passed om bv een object te creeren, en daarna eentje om er iets mee te doen, dan is het mogelijk dat die omgekeerd aankomen. Het oject bestaat dan nog niet, en je krijgt errors.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 04:30

.oisyn

Moderator Devschuur®

Demotivational Speaker

Idd, ik snap ook niet hoe je kunt zeggen dat TCP details nu leren meer werk is dan verder te gaan met een UDP verbinding, wat per definitie meer moeite kost om correct af te handelen dan een TCP verbinding :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • curry684
  • Registratie: Juni 2000
  • Laatst online: 07-05 22:49

curry684

left part of the evil twins

code:
1
WORD wVersionRequested = MAKEWORD( 2, 2 );

Is er ook nog een specifieke reden waarom je alleen WinSock v2.2 wil gebruiken en daarmee een hoop oudere Windows-versies uitsluiten? Als in, volgens mij gebruik je geen enkele functionaliteit die v2.2 nodig heeft :)

Professionele website nodig?


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Wat voor applicatie ben je aan het maken? Want als het gaat om bv gebruikers die op een remote gui knop drukken, gebruik dan zeker TCP. Bij incidentele gebeurtenissen altijd TCP gebruiken, het scheelt je een hoop gedoe. De enige rede om UDP te gebruiken die ik kan bedenken is voor snel wisselende data, die je continu moet synchronizen. Verder moet het niet erg zijn als er soms iets fout gaat. Client posities in een 3d spel wereld bv. Maar niet een temperatuur meting in een kern centrale. En ook niet auto posities op een geautomatiseerde snelweg met robot autos. In die gevallen moet je aan je UDP packets een sequence number toevoegen (om out-of-order arrival te voorkomen) en vervolgens een acknowledgements systeem om het reliable te maken. Tada! Je hebt TCP uitgevonden, en waarschijnlijk bugged jouw implementatie.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Serieus: als je echt betrouwbare message-oriented communicatie nodig hebt, dan moet je ook geen TCP gebruiken. Ja, het is beter dan UDP, maar nog steeds niet geschikt voor de genoemde doelen. TCP is nuttig als resend volstaat als error recovery schema, maar overdrijf het niet. Voor real-time measurements is resend nooit het goede antwoord.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

MSalters schreef op maandag 11 april 2005 @ 13:12:
Serieus: als je echt betrouwbare message-oriented communicatie nodig hebt, dan moet je ook geen TCP gebruiken. Ja, het is beter dan UDP, maar nog steeds niet geschikt voor de genoemde doelen. TCP is nuttig als resend volstaat als error recovery schema, maar overdrijf het niet. Voor real-time measurements is resend nooit het goede antwoord.
True. Maar het ging om het voorbeeld. TCP mag dan niet altijd perfect zijn, maar UDP is dat vrijwel nooit.

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 09-04 22:08
Voor een heleboel real-time toepassingen is het goed genoeg, als je een safe shutdown hebt. Automatische snelweg en een communicatieprobleem? Aan de kant en wachten tot je communicatie wel weer werkt. Daarvoor gebruik je dus UDP, de hele recovery van TCP verbergt alleen maar problemen die je juist moet weten.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Met UDP zie je die problemen dus juist niet. Tenzij je extra checks in gaat bouwen; en dan kom je heel snel op TCP uit. Je kan dan ook een TCP driver iets aanpassen, dat je waarschuwingen binnen krijgt bij mogelijke fouten, en dan beslist of je recovered of shutdowned. In dit geval ligt het dus in het midden. Je wilt meer controle dan UDP kan geven, maar je wilt weer niet de impliciete recovery van TCP. Als je een hele zooi sequence checks etc in gaat bouwen boven op UDP, dan noem ik dat geen UDP meer.

  • froggie
  • Registratie: November 2001
  • Laatst online: 20-11-2024

froggie

Kwaaak

.oisyn schreef op vrijdag 01 april 2005 @ 12:59:
Je zou ipv een seperator ook kunnen aangeven hoeveel bytes deze "message" in beslag gaat nemen. Dan weet je ook vantevoren hoeveel je uit moet lezen en dus op hoeveel verzonden bytes je moet wachten, ipv dat je de stream moet gaan scannen naar je seperator.
Heb je misschien links naar lectuur met dit onderwerp? Want ik ben een soort control protocol aan het ontwerpen om op remote machines taken uit te voeren en de de status van deze taken bij te houden. Ik loop echter tegen hetzelfde probleem aan als de TS, ik heb moeite met de berichten in m'n buffer te onderscheiden.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 04:30

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dat is toch niet zo moeilijk? Je stuurt eerst gewoon een getal in binaire vorm, bijvoorbeeld een 16 bits int, waarmee je aangeeft hoeveel bytes er nog gaan komen voor dit bericht. Vervolgens weet je dat de volgende x bytes bij het bericht horen, en dat daarna weer een getal komt om aan te geven hoe lang het volgende bericht gaat zijn, etc.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

.oisyn schreef op dinsdag 12 april 2005 @ 10:46:
Dat is toch niet zo moeilijk? Je stuurt eerst gewoon een getal in binaire vorm, bijvoorbeeld een 16 bits int, waarmee je aangeeft hoeveel bytes er nog gaan komen voor dit bericht.
Vergeet dan ook niet op je byte ordering te letten (TS). Dus ntohl() en htonl() etc gebruiken...

  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

Je kunt toch ook een delimiter gebruiken?
Bijvoorbeeld:
code:
1
|)blaat blaat blaat(||)froep melp(]

Elk bericht begint met |) en eindigt met (| de berichten onderscheiden gaat dan eenvoudig door te beginnen met lezen bij een |) en door te gaan tot je een (| tegen komt.

Nu met Land Rover Series 3 en Defender 90


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 02-05 01:32
MTWZZ: dat is dikke ellende; dan moet je die gekke codes die in berichten voorkomen weer gaan zitten escapen en unescapen, wat betekent dat je elk bericht nog een keer moet doorlopen en kopiëren. Dan is .oisyn's voorstel een stuk zinniger, vooral ook omdat je van te voren een buffer van de juiste grootte kunt alloceren en daar de data in kunt ontvangen (niets meer kopiëren dus).

Dat heeft trouwens ook wel wat problemen (je moet bijvoorbeeld opletten voor buffer overflows, denial of service attacks, e.d., omdat de client buffergroottes kan bepalen), maar door een beetje na te denken (of je simpelweg te beperken tot 16-bits unsigned integers zoals .oisyn al zei) kun je dat wel voorkomen.
Zoijar schreef op dinsdag 12 april 2005 @ 11:35:
Vergeet dan ook niet op je byte ordering te letten (TS). Dus ntohl() en htonl() etc gebruiken...
Voor shorts zijn dat dus ntohs() en htons(). ;)

[ Voor 9% gewijzigd door Soultaker op 12-04-2005 11:57 ]


  • farlane
  • Registratie: Maart 2000
  • Laatst online: 07-05 10:04
MTWZZ schreef op dinsdag 12 april 2005 @ 11:43:
Je kunt toch ook een delimiter gebruiken?
Bijvoorbeeld:
code:
1
|)blaat blaat blaat(||)froep melp(]

Elk bericht begint met |) en eindigt met (| de berichten onderscheiden gaat dan eenvoudig door te beginnen met lezen bij een |) en door te gaan tot je een (| tegen komt.
Ja als je separator niet in de data kan voorkomen kan dat, maar je getallen in ascii vorm versturen is erg onvoordelig.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


  • froggie
  • Registratie: November 2001
  • Laatst online: 20-11-2024

froggie

Kwaaak

Dat met die berichtlengte versturen is zo'n gek idee nog niet :) Door eerst steeds een 8 bit te versturen die de opdracht aangeeft, gevolgd door bijv nog eens 16 bit die de lengte van de data aangeeft is het heel eenvoudig om de boel uit elkaar te houden.
Het enige wat je feitelijk nog moet doen is je data ontcijferen en je de boodschap interpreteren.
Soultaker schreef op dinsdag 12 april 2005 @ 11:56:
Dat heeft trouwens ook wel wat problemen (je moet bijvoorbeeld opletten voor buffer overflows, denial of service attacks, e.d., omdat de client buffergroottes kan bepalen), maar door een beetje na te denken (of je simpelweg te beperken tot 16-bits unsigned integers zoals .oisyn al zei) kun je dat wel voorkomen.
Als het goed is laat je je client niet je buffergrote bepalen maar alleen de hoeveelheid die je uit de buffer gaat lezen (aangegeven door die waarde die de lengte van het bericht aangeeft). Zolang je zorgt dat die waarde jou buffergrote niet overschreidt is er geen probleem.
De enige valkuil die ik op het moment nog zie is wanneer een bericht een andere waarde opgeeft dan dat het bericht daadwerkelijk lang is. Dit zou eventueel weer op te lossen zijn met zo'n start codon aan het begin van je bericht zodat je programma de draad weer kan oppakken bij het volgende bericht. Toch nog maar eens over nadenken :)

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Je gaat er natuurlijk niet van uit dat je buffer grootte beperkt wordt door een 'short'. Dat is vragen om prolemen... gewoon even een check if (received > buffersize) throw std::range_error() oid.
Pagina: 1