Un server UDP functioneaza conform schemei urmatoare
1. WSAStartup() -->
Initializarea
2.
socket() -->
Obtinerea unui socket
3. Alegerea portului si interfetei de serviciu
4. bind() -->
Legarea serverului la un port 5. Testarea sosirii datelor
6. recvfrom() -->
Citirea mesajului
7.
sendto() -->
Trimiterea raspunsului
8. goto 5
--> se revine la
punctul 5.
Secventa de apeluri anterioara este conforma modelului de aici.
WSAStartup()
Daca solicitam versiunea 2.0 a DLL-ului care implementeaza
socketurile putem apela functia WSAStartup() spre exemplu prin linia
de cod
retval= WSAStartup(majorV+minorV*256,wsockinfo);
Aceasta functie are nevoie ca in zona de date sa fie facute urmatoarele declaratii:
int retval,minorV=0, majorV=2;
LPWSADATA wsockinfo;
unde despre LPWSADATA gasim in fisierul antet winsock.h
urmatoarele:
#define WSADESCRIPTION_LEN 256
#define WSASYS_STATUS_LEN 128
typedef struct WSAData {
WORD wVersion;
WORD wHighVersion;
char szDescription[WSADESCRIPTION_LEN+1];
char szSystemStatus[WSASYS_STATUS_LEN+1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorInfo;
} WSADATA;
typedef WSADATA FAR *LPWSADATA;
Pentru structura WSADATA pointata de wsockinfo
trebuie sa alocam spatiu de exemplu prin linia de cod:
wsockinfo =(LPWSADATA) malloc(sizeof(WSADATA));
Nota. Un apel corect il putem obtine si prin codul
#define majorV 2
#define minorV 0
WSADATA wsaData;
int retval;
WORD vers=majorV+minorV*256;
...
retval = WSAStartup( vers, &wsaData );
Pentru un sistem care are DLL-ul pentru socketuri din 2000 in structura wsaData
se va reintoarce informatia urmatoare
wVersion <-- 2.0
wHighVersion <-- 2.2
szDescription <-- "Microsoft wsock32.dll, ver2.2, 32bit of Jun 7 2000, at 21:34:15."
szSystemStatus <-- "On Win95."
iMaxSockets <-- 32767
iMaxUdpDg <-- 65467
socket()
Apelul functiei socket trebuie facut de forma
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
Ea necesita definirea in zona de date a codului
SOCKET sock;
Inainte de apelul functiei bind() care necesita sintaxa
int bind ( SOCKET s,
const struct sockaddr FAR* name,
int namelen
)
trebuie initializata structura de tipul sockadr care este
descrisa in winsock.h prin
/*
* Structure used by kernel to store most
* addresses.
*/
struct sockaddr {
u_short sa_family; /* address family */
char sa_data[14]; /* up to 14 bytes of direct address */
};
Pentru a putea folosi rutinele htons() si inet_addr()
aceasta structura trebuie echivalata cu structura originala Berkeley
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
in care structura in_addr este descrisa in winsock.h
prin
/*
* Internet address (old style... should be updated)
*/
struct in_addr {
union {
struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
struct { u_short s_w1,s_w2; } S_un_w;
u_long S_addr;
} S_un;
#define s_addr S_un.S_addr
/* can be used for most tcp & ip code */
#define s_host S_un.S_un_b.s_b2
/* host on imp */
#define s_net S_un.S_un_b.s_b1
/* network */
#define s_imp S_un.S_un_w.s_w2
/* imp */
#define s_impno S_un.S_un_b.s_b4
/* imp # */
#define s_lh S_un.S_un_b.s_b3
/* logical host */
};
Echivalarea celor doua structuri se poate face, de exemplu, prin declaratia
union sockadd{
struct sockaddr_in s_i;
struct sockaddr s;
} sa;
Initializarea structurii sockaddr_in presupune definirea
portului de serviciu si a interfetei de serviciu care se poate face de exemplu
prin
char * ip="192.168.1.10";//definit in zona de date
...
sa.s_i.sin_family = PF_INET;
sa.s_i.sin_port = htons(5003);
sa.s_i.sin_addr.s_addr= inet_addr (ip);
in care inet_addr() are sintaxa
unsigned long inet_addr (
const char FAR * cp
);
iar htons() sintaxa
u_short htons ( u_short hostport );
In exemplul de mai sus s-a ales portul 5003 si interfata cu
adresa 192.168.1.10.
bind()
Apelul functiei bind care are sintaxa
int bind ( SOCKET s,
const struct sockaddr FAR* name,
int namelen
)
se poate face prin
retval=bind(sock, psa, sizeof(sa.s));
daca s-a definit in zona de date
struct sockaddr FAR *psa= &sa.s;
sau direct prin
retval=bind(sock, &sa.s, sizeof(sa.s));
Serverul trebuie inainte de a citi date sa vada daca sunt date disponibile la
socketul sock. Aceasta se face folosind functia select.
Sintaxa functiei select este urmatoarea
int select (
int nfds,
fd_set FAR * readfds,
fd_set FAR * writefds,
fd_set FAR * exceptfds,
const struct timeval FAR * timeout
);
Ea are nevoie de definirea a trei structuri de tip fd_set si
a unei structuri de tip timeval. Despre structura fd_set
in winsock.h gasim
/*
* Select uses arrays of SOCKETs. These macros manipulate such
* arrays. FD_SETSIZE may be defined by the user before including
* this file, but the default here should be >= 64.
*
* CAVEAT IMPLEMENTOR and USER: THESE MACROS AND TYPES MUST BE
* INCLUDED IN WINSOCK.H EXACTLY AS SHOWN HERE.
*/
#ifndef FD_SETSIZE
#define FD_SETSIZE 64
#endif /* FD_SETSIZE */
typedef struct fd_set {
u_int fd_count; /* how many are SET? */
SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
} fd_set;
iar despre structura timeval gasim
/*
* Structure used in select() call, taken from the BSD file sys/time.h.
*/
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
Prin urmare daca dorim sa testam numai un socket (si anume socketul sock) in zona de date trebuiesc facute declaratiile
#define FD_SETSIZE 1
struct fd_set rd,wr,er;
struct fd_set *prd=&rd,*pwr=&wr,*per=&er;
struct timeval timeout={0,0},*ptimeout=&timeout;
iar in zona de cod se poate introduce
rd.fd_count=1;
rd.fd_array[0]=sock;
wr.fd_count=1;
wr.fd_array[0]=sock;
er.fd_count=1;
er.fd_array[0]=sock;
retval=select (1,prd,pwr,per,ptimeout);
Daca lucrurile merg bine atunci retval este diferit de SOCKET_ERROR
si se poate afla daca s-au primit date prin testarea valorii lui rd.fd_count.
Daca acesta este 1 atunci la socketul sock au venit date.
recvfrom()
Citirea datelor se face cu functia recvfrom care are sintaxa
int recvfrom (
SOCKET s,
char FAR* buf,
int len,
int flags,
struct sockaddr FAR* from,
int FAR* fromlen
);
daca in zona de date s-au facut definitiile
#define maxbuff 517
char buffin[maxbuff];
struct sockaddr sa_in;
int adrlun=sizeof(sockaddr);
se poate apela functia recvfrom prin
retval = recvfrom(sock, buffin, maxbuff-1,0, &sa_in, &adrlun );
Daca lucrurile au mers normal in bufferul buffin se afla datele
primite si retval precizeaza numarul de octeti receptionati.
sendto()
Raspunsul la mesajul receptionat se face folosind functia sendto.
Aceasta are sintaxa
int sendto (
SOCKET s,
const char FAR * buf,
int len,
int flags,
const struct sockaddr FAR * to,
int tolen
);
Astfel linia de cod
retval=sendto(sock,buffout,lung,0,&sa_in,sizeof(sa_in));
presupune existenta in zona de date a definitiilor
int lung;
char buffout[maxbuff];
In cele de mai sus se presupune ca datele de trimis sunt memorate in bufferul
buffout si au lungimea egala cu valoarea variabilei lung.
De asemenea se presupune ca adresa corespondentului este memorata in structura sa_in
dupa apelul functiei recvfrom() la punctul 6.
![]()
© Cornel Mironel Niculae, 2004-2005
13-Nov-2004