Sunteți pe pagina 1din 19

Programarea aplicaiilor de timp real

9. Curs Socket
9.1. Introducere
Cursul reprezint o introducere n implementarea comunicaiei ntre procese bazat pe socket. Un socket este un punct final al unei comunicaii, un obiect al unei aplicaii prin care aceasta trimite i recepioneaz date. Prin intermediul lui se realizeaz un schimb de date n regim fullduplex. Un socket are un tip, este asociat unui proces i trebuie s aib un nume. n mod curent socketul schimb date doar cu un alt socket aflat n acelai domeniu de comunicaie i care utilizeaz acelai protocol. Exist dou tipuri de socket: stream; datagram. Socket-ul de tip stream suport un flux de date fr limite sub forma unui ir de octei. Se garanteaz transmiterea i recepionarea corect. Acest tip de socket se mai numete connexion oriented. El utilizeaz protocolul TCP/IP. Socket-ul de tip datagram suport un flux de date orientat spre fiiere. El nu garanteaz livrarea fiierelor. Acest tip de socket se mai numete message oriented. El utilizeaz protocolul UDP. Arhitectura client-server este cea mai potrivit pentru realizarea comunicaiilor bazate pe socket. 9.1.1. Utilizarea firelor pentru evitarea blocrii conexiunii Dac aplicaia client nu are altceva de fcut dect s atepte date atunci nu este necesar s se creeze fire. Dac aplicaia trebuie s rspund i altor evenimente generate de interfaa utilizator, ceea ce este cel mai probabil este necesar s se creeze fire separate pentru citire i scriere. 9.1.2. Despre endianism i transferul datelor Atunci cnd se creeaz o aplicaie pe o anumit platform un programator nu acord mare atenie reprezentrii datelor. Atunci cnd se creeaz o aplicaie pentru reea problema reprezentrii datelor este foarte important. Internetul, n special, a impus o secven standard pentru stocarea datelor numerice referit prin sintagma network byte-order. Spre deosebire, secvena pentru reprezentarea datelor pe calculatorul gazd se numete host byte-order . Aa cum se cunoate, pentru a transmite date de la un sistem la altul, stratul fizic al reelei va transmite un ir de octei prin intermediul firelor de conexiune. Stratul fizic nu modifica secvena de bii pe care o transmite. Dac se transmite numrul 0x1ADE de pe un calculator PC pe un calculator McIntosh, PC-ul, utiliznd little-endian byte-order, va transmite mai nti octetul cel mai puin semnificativ i apoi octetul cel mai semnificativ. La recepie se utilizeaz big-endian byte-order i primul octet va fi interpretat ca octetul cel mai semnificativ. Deci pe Mcintosh numrul trimis de PC va fi interpretat ca 0xDE1A. Pentru a evita asfel de erori Internet-ul definete ca standard de reea big-endian byte-order. Cu alte cuvinte este impus stocarea datelor cu octetul cel mai semnificativ naintea octetului mai puin semnificativ. nainte de a transmite date pe reea este necesar s se fac o transformare a acestora n ordinea big-endian. La recepia datelor de pe reea trebuie s se fac transformarea n ordinea impus de
1

Cap. 9. Socket

platforma pe care se lucreaz. Winsock API ofer o serie de funcii care realizeaz operaiile de transformare a ordinii octeilor. Aceste funcii sunt prezentate n tab. 1.
Tabelul 1. Funcii de conversie Winsock

Funcia Winsock htonl htons ntohl ntohs

Explicaii Convertete un intreg pe 32 de bii din hostbyte order n network-byte oreder Convertete un intreg pe 16 de bii din hostbyte order n network-byte oreder Convertete un intreg pe 32 de bii networkbyte oreder din n host- byte order Convertete un intreg pe 16 de bii networkbyte oreder din n host- byte order

9.1.3. Gsirea adresei IP a unui calculator Programele de comunicaie pentru reea trebuie s cunoasc portul pe care aplicaia client poate comunica cu aplicaia server i adresa IP a calculatorului cu care se dorete comunicarea. Pentru gsirea adresei IP este necesar s se utilizeze funcii asincrone pe care Socket API le are sau s se completeze o structur de date specific cu datele de identificare a adresei IP a serverului.

9.2. Despre Winsock API


Winsock API este o bibliotec de funcii care implementeaz interfaa de tip socket care este cunoscut sub numele de BSD (Berkeley Software Distribution). Programatorii de aplicaii de acest tip sub sistemul Windows trebuie s lege aplicaia de biblioteca WSOCK32.LIB. n cadrul programelor scrise n LabWindows/CVI aceast bibliotec trebuie adugat utiliznd EditAdd Files to Project .. Library (*.lib). Ea se gsete n directorul Program Files\National Instruments\CVI7x\sdk\lib 9.2.1. Socket bazat pe TCP O legtur sigur realizat prin intermediul unui socket este bazat pe TCP. O astfel se legtur trebuie s realizeze conexiune ntre dou procese nainte ca acestea s poat trimite i recepiona date unul de la altul. Datele trimise ntre procese sunt simple fluxuri de octei.. ntr-o interaciune tipic client-server, serverul creeaz un socket, care are un nume i ateapt ca clienii s se conecteze la server. Clientul creeaz i el un socket i se conecteaz la server. Atunci cnd serverul detecteaz conectarea la socket-ul su, acesta creeaz un nou socket i l utilizeaz pentru comunicaia cu clientul. Socket-ul de la server continu s atepte conexiuni de la ali clieni. Interaciunea ntre client i server este prezentat n fig. 9.1. 9.2.2. Funcii de pornire si oprire Funciile Winsock de care are nevoie o aplicaie se gsesc n WSOCK32.DLL. nainte ca aplicaia s utilizeze o funcie aceasta trebuie s cheme o funcie de iniializare WSAStartup(), iar dup terminare este necesar apelarea funciei WSACleanup(). Funcia WSAStartup() iniializeaz WSOCK32.DLL. Acest apel de funcie face posibil utilizarea mai multor implementri ale stivei TCP/IP, in funcie de furnizor i confirm c versiunea WSOCK32.DLL utilizat este compatibil cu aplicaia.

Programarea aplicaiilor de timp real

Fig. 9.1. Interaciunea ntre aplicaiile client-server n cazul unui socket-stream

Prototipul acestei funcii este:


int WSAStartup(WORD wVersionRequested, LPWSADATA lpwsaData);

Parametri: Numele
wVersionRequested lpwsaData

Tipul
Word pointer

Descrierea Reprezint versiunea cea mai mare a Winsock API pe care aplicaia o va utiliza. Pointer ctre o structur care va recepiona detalii ale implementrii WinSock. Cod de eroare. Vezi documentaia CVI

Valoare returnat:
status integer

Cap. 9. Socket

Not:

Pentru a cere versiunea 1.1 se folosete urmtorul cod:


WORD wVersionRequested = MAKEWORD(1,1);

Structura Winsock are urmtorul format:


typedef struct WORD WORD char char unsigned unsigned char FAR } WSADATA; WSAData { wVersion; wHighVersion; szDescription[WSADESCRIPTION_LEN+1]; szSystemStatus[WSA_SYS_STATUS_LEN+1]; short iMaxSockets; short imaxUdpDg; * lpVendorInfo;

Urmtorul fragment de cod arat o implementare tipic:


WSADATA m_wsaData; WORD wVersionRequested = MAKEWORD(1,1); int nRet; . . . . . . . . . . . . . . . . . . . . . . . . . nRet = WSAStartup(wVersionRequested, &m_wsaData); if (m_wsaData.wVersion != wVersionRequested) MessagePopup ("Eroare", "Versiune necorespunzatoare ");

Informaiile coninute n structura WSADATA pot fi afiate n panouri sau controale.

9.3. Crearea unui socket


Crearea unui socket fr nume se face prin apelul funciei socket(). Aceast funcie creeaz un punct final de comunicaie i are urmtorul prototip: Parametri: Numele
af type SOCKET socket(int af, int type, int protocol);

Tipul
int int

Descrierea WinSock1.1 suport doar parametrul AF_INET. Specific tipul de socket. Exist doar dou tipuri de socket suportate de Windows Sockets 1.1: SOCK_STREAM i SOCK_DGRAM. Handle pentru obiect

Valoare returnat:
socket SOCKET

Protocolul nu este precizat n mod explicit deoarece combinaia address family i type, descrie n mod unic protocolul. De exemplu, dac address family este AF_INET i type este SOCK_STREAM, protocolul este TCP, iar dac address family este AF_INET i type este SOCK_DGRAM, protocolul este UDP. La succes funcia ntoarce un descriptor de socket, altfel o valoare INVALID_SOCKET este ntoars i prin apelul unei funcii WSAGetLastError() se pot obine informaii suplimentare asupra tipului de eroare. Printre erorile posibile enumerm: WSANOTINITIALIZED, care apare dac funcia WSAStartup() nu s-a apelat cu succes, WSAENETDOWN, care apare dac reeaua nu este n funciune etc. Urmtorul fragment de cod arat o implementare tipic:
4

Programarea aplicaiilor de timp real

SOCKET listenSocket; listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(listenSocket==INVALID_SOCKET) { MessagePopup ("Eroare", "Eroare la socket()"); closesocket(listenSocket); return 1; }

9.4. Acceptarea unei conexiuni de la client


n aplicaia server, un socket este legat de un nume. Apoi aplicaia server ascult conexiuni pe acest socket. Atunci cnd un client se conecteaz la server, acesta va accepta noua conexiune. n acest punct schimbul de date poate ncepe. NUMIREA UNEI CONEXIUNI Pentru a utiliza un socket creat, trebuie ca acestuia s i se atribuie un nume. Funcia bind() realizeaz acest lucru. Aceast funcie are urmtorul prototip:
int bind(SOCKET s,const struct FAR * addr,int namelen);

Parametri: Numele
s addr namelen

Tipul
SOCKET pointer int

Descrierea Descriptorul de socket Pointer ctre o structur sockaddr cu adresa sau numele care va fi atribuit socketului. Numele atribuit socketului

Not:

Dac funcia a avut succes aceasta ntoarce 0, iar n caz contrar ntoarce SOCKET_ERROR. Prin apelul unei funcii WSAGetLastError() se pot obine informaii suplimentare asupra tipului de eroare.

Structura sockaddr este definit astfel:


struct sockaddr { u_short sa_family; char sa_data[14]; };

// address family // 14 octei pentru adresa directa

Formatul lui sa_data depinde de address family. n cazul WinSock 1.1, doar adresarea de tip Internet este permis. Din acest motiv o structur socksddr_in este definit. Aceasta va fi utilizat n locul lui sockaddr atunci cnd se apeleaz funcia bind(). Forma acestei structuri este urmtoarea:
struct sockaddr_in { short u_short struct in-addr char }

sin_family; sin_port; sin_addr; sin_zero[8];

// // // //

Address family Service port Internet address filler

sin_family trebuie s fie AF_INET. Valoarea sin_port trebuie dat n network byte-order. Valoarea sin_addr poate fi dat n trei forme diferite: ca patru octei, ca doi short sau ca un long.
5

Cap. 9. Socket

Forma structurii sin_addr este urmtoarea:


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; #define s_host S_un.s_un_b.s_b2; #define s_net s_un.s_un_b.s_b1; #define s_imp S_un.s_un_w.s_w2; #define s_impno S_un.s_un_b.s_b4; #define s_lh S_un.s_un_b.s_b3; };

Urmtorul fragment de cod arat o implementare tipic:

SOCKADDR_IN saServer; int nPort, nRet; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . // Construirea structurii numelui saServer.sin_family=AF_INET; saServer.sin_addr.s_addr=INADDR_ANY;//Adresa este pusa de WINSOCK nPort=1200; saServer.sin_port=htons(nPort); // Numarul portului // Asocierea de un port nRet=bind(listenSocket, (LPSOCKADDR) &saServer, sizeof(struct sockaddr)); if(nRet==SOCKET_ERROR) { MessagePopup ("Eroare", "Eroare la bind()"); closesocket(listenSocket); return 1; }

ASCULTAREA UNEI CONEXIUNI Etapa urmtoare este de a pune n ascultare socketul creat n ateptarea unei conexiuni de la clieni. Acest lucru se face cu funcia listen(). Prototipul aceste funcii este:
int listen(SOCKET s, int backlog);

Parametri: Numele
s backlog

Tipul
SOCKET int

Descrierea Descriptorul de socket Contor al conexiunilor ateptate

Not:

Dac funcia a avut succes aceasta ntoarce 0, iar n caz contrar ntoarce SOCKET_ERROR. Prin apelul unei funcii WSAGetLastError() se pot obine informaii suplimentare asupra tipului de eroare.

Urmtorul fragment de cod arat o implementare tipic:


6

Programarea aplicaiilor de timp real

// Seteaza socketul sa asculte nRet = listen(listenSocket, SOMAXCONN); if (nRet == SOCKET_ERROR) { MessagePopup ("Eroare", "Eroare la listen()"); closesocket(listenSocket); return 2; }

ACCEPTAREA UNEI CONEXIUNI Urmtoarea operaie este acceptarea unei conexiuni de la client. Funcia accept() realizeaz acest lucru. Prototipul aceste funcii este:
SOCKET accept(SOCKET s, struct socksddr FAR *addr, int FAR *addrlen)

Parametri: Numele
s addr

Tipul
SOCKET pointer

Descrierea Descriptorul de socket Pointer la o structur sockaddr, n care se recepioneaz adresa clientului conectat. Se poate transfera un parametru NULL Lungimea structurii addr. Dac s-a transferat addr ca un pointer NULL atunci i addrlen trebuie s fie nul.

addrlen

int

Not:

Dac funcia a avut succes aceasta ntoarce 0, iar n caz contrar ntoarce SOCKET_ERROR. Prin apelul unei funcii WSAGetLastError() se pot obine informaii suplimentare asupra tipului de eroare.

Urmtorul fragment de cod arat o implementare tipic:


remoteSocket = accept(listenSocket, // Socketul asteptat NULL, // Optional client address NULL); if (remoteSocket == INVALID_SOCKET) { MessagePopup ("Eroare", "Eroare la accept()"); closesocket(listenSocket); return 4; }

Acest mod de acceptare a unei conexiuni realizeaz blocarea aplicaiei server pn la apariia unei cereri de conectare de la un client. Exist i o alt modalitate de conectare, fr blocare, prin funcia WSAAsyncSelect(), pe care nu o discutm aici, deoarece implementarea ei n LabWindows/CVI este destul de greoaie.

9.5. Conectarea unui client la server


Clientul trebuie s creeze un socket i s se conecteze la server. Crearea unui socket se realizeaz tot cu funcia socket(), pe care am discutat-o mai sus, iar conectarea se realizeaz cu funcia connect(), care urmtorul prototip:
int connect(SOCKET s, const struct socksddr FAR *name, int namelen) 7

Cap. 9. Socket

Parametri: Numele
s name

Tipul
SOCKET pointer

Descrierea Descriptorul de socket Pointer la o structur sockaddr, n care se completeaz adresa serverului la care se va conecta clientul; Lungimea structurii sockaddr.

namelen

int

Urmtorul fragment de cod arat o implementare tipic:


SOCKET ClientSocket; SOCKADDR_IN saServer; int nPort; char cPort_Adr[14]; . . . . . . . . . . . . . . . . . . . . . . . . saServer.sin_family = AF_INET; GetCtrlVal(panelHandle, PANEL_NUMERIC, &nPort); saServer.sin_port=htons(nPort); GetCtrlVal(panelHandle, PANEL_STRING, cPort_Adr); a=strtok(cPort_Adr, "."); saServer.sin_addr.S_un.S_un_b.s_b1=(unsigned char)atoi(a); a=strtok(NULL, "."); saServer.sin_addr.S_un.S_un_b.s_b2=(unsigned char)atoi(a); a=strtok(NULL, "."); saServer.sin_addr.S_un.S_un_b.s_b3=(unsigned char)atoi(a); a=strtok(NULL, "."); saServer.sin_addr.S_un.S_un_b.s_b4=(unsigned char)atoi(a); nRet = connect(ClientSocket, (LPSOCKADDR)&saServer, sizeof(struct sockaddr)); if (nRet == SOCKET_ERROR) { MessagePopup ("Eroare", "Eroare la connect()"); closesocket(ClientSocket); return 2; }

n cadrul acestui fragment de cod s-a presupus c nPort i structura saServer s-au preluat din dou controale ale aplicaiei, unul numeric i unul de tip STRING, iar completarea structurii s-a realizat utiliznd funcia htons(), de care am mai vorbit i strtok(), care este documentat n LabWindows/CVI. Adresa serverului va fi completat n control sub forma clasic, adic cu punct ntre octeii adresei.

9.6. Transmiterea i recepionarea datelor


Transmiterea datelor n cadrul aplicaiilor client-server bazate pe socket se face utiliznd funcia send(). Aceast funcie are urmtorul prototip:
int send(SOCKET s,const struct FAR * buff,int len, int flags);

Parametri: Numele
s
8

Tipul
SOCKET

Descrierea Descriptorul de socket

Programarea aplicaiilor de timp real

buff len flags

pointer int int

Pointer ctre o structur n care se introduc datele de transmis; Lungimea buferului de date; Specific modul de transmitere.

Not:

Dac funcia a avut succes aceasta ntoarce numrul de octei transmii, iar n caz contrar ntoarce SOCKET_ERROR. Prin apelul unei funcii WSAGetLastError() se pot obine informaii suplimentare asupra tipului de eroare.

Urmtorul fragment de cod arat o implementare tipic:


SOCKET Socket; char szBuf1[256]; int nRet; . . . . . . . . . . . . . . . . . . . . . . . . . . . nRet = send(Socket, szBuf1, strlen(szBuf1), 0); if (nRet == SOCKET_ERROR) { MessagePopup ("Eroare", "Eroare la send()"); closesocket(Socket); return 5; }

Recepionarea datelor n cadrul aplicaiilor client-server bazate pe socket se face utiliznd funcia recv(). Aceast funcie are urmtorul prototip:
int recv(SOCKET s,const struct FAR * buff,int len, int flags);

Parametri: Numele
s buff len flags

Tipul
SOCKET pointer int int

Descrierea Descriptorul de socket Pointer ctre o structur n care se introduc datele de transmise; Lungimea buferului de date; Specific modul de recepie.

Not:

Dac funcia a avut succes aceasta ntoarce numrul de octei recepionai iar n caz contrar ntoarce SOCKET_ERROR. Prin apelul unei funcii WSAGetLastError() se pot obine informaii suplimentare asupra tipului de eroare.

Urmtorul fragment de cod arat o implementare tipic:


SOCKET Socket; char szBuf1[256]; int nRet; . . . . . . . . . . . . . . . . . . . . . . . . . . . nRet = recv(Socket, szBuf1, strlen(szBuf1), 0); if (nRet == SOCKET_ERROR) {
9

Cap. 9. Socket

MessagePopup ("Eroare", "Eroare la recv()"); closesocket(Socket); return 5;

La crearea unor aplicaii client-server n care serverul se blocheaz la accept(), este indicat ca transmiterea s se fac sub controlul unui buton, iar recepia s se fac n cadrul unui fir la fiecare aplicaie. Exemplu Se vor crea dou aplicaii:

Fig. 9.2. Interfaa utilizator a serverului

aplicaia server, care s aib interfaa prezentat n fig. 9.2. Aplicaia va realiza sub controlul unor butoane distincte operaiile de creare a unui socket, legare (bind), ascultare (listen). La apsarea butonului accept serverul se va bloca n rutina accept(). Ieirea din aceasta are loc numai dup ce un client se va conecta la server. Atunci se va crea un fir de recepie a datelor de la client. Interfaa utilizator conine controale de tip TEXT BOX i LISTBOX n care vor apare evenimente, informaia care urmeaz s fie transmis i respectiv informaiile transmise ntre client i server. Dac apare o cerere de conexiune de la un client se va aprinde un led pe interfaa utilizator. Butoanele se vor valida sau invalida pentru a permite realizarea unei succesiuni corecte a operaiilor necesare. La apsarea butonului bind va trebui precizat portul pe care se leag serverul.

10

Programarea aplicaiilor de timp real

Fig. 9.3. Interfaa utilizator a clientului

aplicaia client, care s aib interfaa prezentat n fig. 9.3. Aplicaia va realiza sub controlul unor butoane distincte operaiile de creare a unui socket i conectare la server. Adresa serverului i portul trebuie precizate prin completarea controalelor corespunztoare ale interfeei. Codul programului pentru server este urmtorul:
#include #include #include #include #include #include <cvirte.h> <userint.h> "server_socket.h" <winsock2.h> <windows.h> <formatio.h>

static int panelHandle, panel1, panel2; static DWORD dwThreadID; static HANDLE hThread; SOCKET listenSocket; WSADATA m_wsaData; WORD wVersionRequested = MAKEWORD(1,1); SOCKADDR_IN saServer; int nPort, nLineTextRec=0, nLineTextSend=0; char szBuffRecv[256], szBuffSend[256]; static DWORD dwThreadID; static HANDLE hThread; SOCKET remoteSocket; char mes[256]; int nIndex=0, nItem1=0, nItem=0; DWORD WINAPI Thread(LPVOID data);

11

Cap. 9. Socket

int main (int argc, char *argv[]) { if(InitCVIRTE (0, argv, 0) == 0) return -1; /* out of memory */ if((panelHandle = LoadPanel (0,"server_socket.uir",PANEL))< 0) return -1; DisplayPanel (panelHandle); SetCtrlVal(panelHandle, PANEL_LED, 0); RunUserInterface (); DiscardPanel (panelHandle); WSACleanup(); return 0; } int CVICALLBACK Main_Win (int panel, int event, void *callbackData, int eventData1, int eventData2) { switch (event) { case EVENT_CLOSE: TerminateThread(hThread, 0); closesocket(listenSocket); closesocket(remoteSocket); QuitUserInterface (0); break; } return 0; } int CVICALLBACK Creare (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) { int nRet; switch (event) { case EVENT_COMMIT: nRet = WSAStartup(wVersionRequested, &m_wsaData); if (m_wsaData.wVersion != wVersionRequested) MessagePopup ("Eroare", "Versiune necorespunzatoare "); listenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(listenSocket==INVALID_SOCKET) { MessagePopup ("Eroare", "Eroare la socket()"); closesocket(listenSocket); return 1; } SetCtrlVal(panelHandle, PANEL_TEXTBOX, "S-a creat un socket pentru legatura cu clientii\n"); SetCtrlAttribute(panelHandle, PANEL_C3, ATTR_DIMMED, 0); SetCtrlAttribute(panelHandle, PANEL_C1, ATTR_DIMMED, 1); SetCtrlAttribute(panelHandle, PANEL_C2, ATTR_DIMMED, 0); break; } return 0; } int CVICALLBACK Af_Info (int panel, int control, int event,
12

Programarea aplicaiilor de timp real

void *callbackData, int eventData1, int eventData2) char stri[40]; switch (event) { case EVENT_COMMIT: panel1 = LoadPanel (panelHandle, "server_socket.uir, PANEL1); InstallPopup (panel1); Fmt (stri, "%d.%d", (int)HIBYTE(m_wsaData.wVersion), (int)LOBYTE(m_wsaData.wVersion)); SetCtrlVal(panel1, PANEL1_STRING, stri); SetCtrlVal(panel1, PANEL1_STRING1, m_wsaData.szDescription); SetCtrlVal (panel1, PANEL1_STRING2, m_wsaData.szSystemStatus); Fmt (stri, "%s<%d", m_wsaData.iMaxSockets); SetCtrlVal (panel1, PANEL1_STRING3, stri); Fmt (stri, "%s<%d", m_wsaData.iMaxUdpDg); SetCtrlVal (panel1, PANEL1_STRING4, stri); break; } return 0;

int CVICALLBACK Panel1_Main(int panel, int event, void *callbackData, int eventData1, int eventData2) { switch (event) { case EVENT_CLOSE: RemovePopup (0); break; } return 0; } int CVICALLBACK binds (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) { switch (event) { case EVENT_COMMIT: panel2 = LoadPanel (panelHandle, "server_socket.uir", PANEL2); InstallPopup (panel2); SetCtrlAttribute(panelHandle, PANEL_C4, ATTR_DIMMED, 0); SetCtrlAttribute(panelHandle, PANEL_C3, ATTR_DIMMED, 1); break; } return 0; } int CVICALLBACK bind2 (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) { int nRet; char stri[20];
13

Cap. 9. Socket

switch (event) { case EVENT_COMMIT: // Construirea structurii numelui saServer.sin_family=AF_INET; saServer.sin_addr.s_addr=INADDR_ANY; // Adresa este pusa de WINSOCK GetCtrlVal(panel2, PANEL2_NUMERIC, &nPort); saServer.sin_port=htons(nPort); // Numarul portului // Asocierea de un port nRet=bind(listenSocket, (LPSOCKADDR) &saServer, sizeof(struct sockaddr)); if(nRet==SOCKET_ERROR) { MessagePopup ("Eroare", "Eroare la bind()"); closesocket(listenSocket); return 1; } RemovePopup (0); SetCtrlVal(panelHandle, PANEL_TEXTBOX, "Serverul"); SetCtrlVal(panelHandle, PANEL_TEXTBOX, " asteapta legatura cu clientii pe portul "); Fmt (stri, "%d", nPort); SetCtrlVal(panelHandle, PANEL_TEXTBOX, stri); SetCtrlVal(panelHandle, PANEL_TEXTBOX, "\n"); break; } return 0;

int CVICALLBACK listen1 (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) { int nRet; switch (event) { case EVENT_COMMIT: // Seteaza socketul sa asculte nRet = listen(listenSocket, SOMAXCONN); if (nRet == SOCKET_ERROR) { MessagePopup ("Eroare", "Eroare la listen()"); closesocket(listenSocket); return 2; } SetCtrlVal(panelHandle, PANEL_TEXTBOX, "Serverul este setat sa asculte conexiuni de la clienti\n"); SetCtrlAttribute(panelHandle, PANEL_C5, ATTR_DIMMED, 0); SetCtrlAttribute(panelHandle, PANEL_C4, ATTR_DIMMED, 1); break; } return 0; } int CVICALLBACK accept1 (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) {
14

Programarea aplicaiilor de timp real

switch (event) { case EVENT_COMMIT: // Asteptarea unei cereri SetCtrlVal(panelHandle, PANEL_TEXTBOX, "Serverul blocat la accept asteptand conexiuni de la clienti\n"); // Socketul asteptat remoteSocket = accept(listenSocket, NULL, NULL); if (remoteSocket == INVALID_SOCKET) { MessagePopup ("Eroare", "Eroare la accept()"); closesocket(listenSocket); return 4; } SetCtrlVal(panelHandle, PANEL_LED, 1); SetCtrlVal(panelHandle, PANEL_TEXTBOX, "S-a creat firul de receptie al clientului\n"); hThread=CreateThread(NULL,0,Thread,NULL,0,&dwThreadID); break; } return 0; } int CVICALLBACK send1 (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) { int nRet; char mes[256]="Server: "; switch (event) { case EVENT_COMMIT: // -------------------------------------------------------------memset(szBuffSend, 0, sizeof(szBuffSend)); GetCtrlVal(panelHandle, PANEL_TEXTBOX1, szBuffSend); strcat (mes, szBuffSend); // mesaj de afisat InsertListItem(panelHandle, PANEL_LISTBOX,nItem,mes,0); GetCtrlAttribute (panelHandle, PANEL_LISTBOX, ATTR_CTRL_INDEX, &nItem); SetCtrlAttribute (panelHandle, PANEL_LISTBOX, ATTR_HILITE_CURRENT_ITEM, 1); CheckListItem (panelHandle, PANEL_LISTBOX, nItem, 1); DeleteTextBoxLines (panelHandle, PANEL_TEXTBOX1, 0, -1); SetActiveCtrl (panelHandle, PANEL_TEXTBOX1); // --------------------------------------------------------------nRet =send(remoteSocket,szBuffSend,strlen(szBuffSend), 0); if (nRet == SOCKET_ERROR) { MessagePopup ("Eroare", "Eroare la send()"); closesocket(remoteSocket); return 5; } SetCtrlVal (panelHandle, PANEL_TEXTBOX, "S-a tansmis un mesaj catre soketul client\n"); break; } return 0;
15

Cap. 9. Socket

} DWORD WINAPI Thread(LPVOID data) { int nRet; while(1) { char mes[256]= "Client: "; memset(szBuffRecv, 0, sizeof(szBuffRecv)); nRet = recv(remoteSocket,szBuffRecv, sizeof(szBuffRecv),0); if (nRet == INVALID_SOCKET) { SetCtrlVal(panelHandle, PANEL_LED, 0); closesocket(listenSocket); closesocket(remoteSocket); return 5; } // -----------------------------------------------------------// Afiseaza datele receptionate strcat (mes, szBuffRecv); InsertListItem(panelHandle,PANEL_LISTBOX nItem, mes, 0); GetCtrlAttribute (panelHandle, PANEL_LISTBOX, ATTR_CTRL_INDEX, &nItem); SetCtrlAttribute (panelHandle, PANEL_LISTBOX, ATTR_HILITE_CURRENT_ITEM, 1); CheckListItem (panelHandle, PANEL_LISTBOX, nItem, 1); SetCtrlVal (panelHandle, PANEL_TEXTBOX, "S-a primit un mesaj de la soketul client\n"); } }

Codul programului pentru client este urmtorul:


#include #include #include #include #include #include <cvirte.h> <userint.h> <winsock2.h> <windows.h> <ansi_c.h> "client_socket.h"

static int panelHandle, panel1, panel2; static DWORD dwThreadID; static HANDLE hThread; SOCKET ClientSocket; WSADATA m_wsaData; WORD wVersionRequested = MAKEWORD(1,1); SOCKADDR_IN saClient; char szBuf[256], szBuf1[256]; int nLineText=0; char mes[256]; int nItem=0; DWORD WINAPI Thread(LPVOID data); int main (int argc, char *argv[]) {
16

Programarea aplicaiilor de timp real

if (InitCVIRTE (0, argv, 0) == 0) return -1; /* out of memory */ if ((panelHandle = LoadPanel(0,"client_socket.uir",PANEL))<0) return -1; DisplayPanel (panelHandle); RunUserInterface (); DiscardPanel (panelHandle); WSACleanup(); return 0;

int CVICALLBACK Main_Win (int panel, int event, void *callbackData, int eventData1, int eventData2) { switch (event) { case EVENT_CLOSE: TerminateThread(hThread, 0); closesocket(ClientSocket); QuitUserInterface (0); break; } return 0; } int CVICALLBACK Socket1 (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) { int nRet; switch (event) { case EVENT_COMMIT: // Crearea unui socket client nRet = WSAStartup(wVersionRequested, &m_wsaData); if (m_wsaData.wVersion != wVersionRequested) MessagePopup ("Eroare", "Versiune necorespunzatoare "); ClientSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(ClientSocket==INVALID_SOCKET) { MessagePopup ("Eroare", "Eroare la socket()()"); closesocket(ClientSocket); return 1; } SetCtrlVal(panelHandle, PANEL_TEXTBOX, "S-a creat un socket client\n"); break; } return 0; } int CVICALLBACK Connect1 (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) { unsigned int nPort; int nRet; char cPort_Adr[14]; char *a; switch (event) { case EVENT_COMMIT:
17

Cap. 9. Socket

// Copletarea structurii saClient saClient.sin_family = AF_INET; GetCtrlVal(panelHandle, PANEL_NUMERIC, &nPort); saClient.sin_port=htons(nPort); GetCtrlVal(panelHandle, PANEL_STRING, cPort_Adr); a=strtok(cPort_Adr, "."); saClient.sin_addr.S_un.S_un_b.s_b1=(unsigned char)atoi(a); a=strtok(NULL, "."); saClient.sin_addr.S_un.S_un_b.s_b2=(unsigned char)atoi(a); a=strtok(NULL, "."); aClient.sin_addr.S_un.S_un_b.s_b3=(unsigned char)atoi(a); a=strtok(NULL, "."); saClient.sin_addr.S_un.S_un_b.s_b4=(unsigned char)atoi(a); nRet = connect(ClientSocket,(LPSOCKADDR)&saClient, sizeof(struct sockaddr)); if (nRet == SOCKET_ERROR) { MessagePopup ("Eroare", "Eroare la connect()"); closesocket(ClientSocket); return 2; } SetCtrlVal(panelHandle, PANEL_TEXTBOX, "Clientul este conectat la serverul ales\n"); hThread=CreateThread(NULL, 0, Thread, NULL, 0, &dwThreadID); SetCtrlVal(panelHandle, PANEL_TEXTBOX, "S-a creat firul de receptie al serverului\n"); break; } return 0; } int CVICALLBACK Transmite (int panel, int control, int event, void *callbackData, int eventData1, int eventData2) { int nRet; char mes[256]="Client: "; switch (event) { case EVENT_COMMIT: memset(szBuf1, 0, sizeof(szBuf1)); // mesaj de transmis GetCtrlVal(panelHandle, PANEL_TEXTBOX1, szBuf1); strcat (mes, szBuf1); // mesaj de afisat InsertListItem(panelHandle,PANEL_LISTBOX,nItem, mes, 0); GetCtrlAttribute (panelHandle, PANEL_LISTBOX, ATTR_CTRL_INDEX, &nItem); SetCtrlAttribute (panelHandle, PANEL_LISTBOX, ATTR_HILITE_CURRENT_ITEM, 1); CheckListItem (panelHandle, PANEL_LISTBOX, nItem, 1); DeleteTextBoxLines (panelHandle, PANEL_TEXTBOX1, 0, -1); nRet = send(ClientSocket, szBuf1, strlen(szBuf1), 0); if (nRet == SOCKET_ERROR) { MessagePopup ("Eroare", "Eroare la send()"); closesocket(ClientSocket); return 5; } SetActiveCtrl (panelHandle, PANEL_TEXTBOX1);
18

Programarea aplicaiilor de timp real

SetCtrlVal (panelHandle, PANEL_TEXTBOX, "S-a trimis un mesaj catre server\n"); break; } return 0; } DWORD WINAPI Thread(LPVOID data) { int nRet; while(1) { char mes[256]="Server: "; memset(szBuf, 0, sizeof(szBuf)); nRet = recv(ClientSocket, szBuf, sizeof(szBuf), 0); if (nRet == SOCKET_ERROR) { MessagePopup ("Eroare", "Eroare la recv()"); closesocket(ClientSocket); return 3; } strcat (mes, szBuf); InsertListItem(panelHandle,PANEL_LISTBOX,nItem, mes, 0); GetCtrlAttribute (panelHandle, PANEL_LISTBOX, ATTR_CTRL_INDEX, &nItem); SetCtrlAttribute (panelHandle, PANEL_LISTBOX, ATTR_HILITE_CURRENT_ITEM, 1); CheckListItem (panelHandle, PANEL_LISTBOX, nItem, 1); SetCtrlVal (panelHandle, PANEL_TEXTBOX, "S-a primit un mesaj de la server\n"); } }

19

S-ar putea să vă placă și