Sunteți pe pagina 1din 12

Laborator 4

Data Transmission

21. send() and WSASend()

22. WSASendDisconnect()

23. Out-of-Band Data

24. recv() and WSARecv()

25. WSARecvDisconnect()

26. Stream Protocols

27. Scatter-Gather I/O

28. Breaking the Connection

29. shutdown()

30. closesocket()

31. TCP Receiver/Server With select() Example

Data Transmission

Trimiterea şi primirea datelor este obiectivul principal al programarii in retea. Pentru a trimite date pe un socket conectat, există două funcţii API: send () şi WSASend ().A doua funcţie este specifica pentru Winsock 2. De asemenea, două funcţii sunt pentru primirea de date pe un socket conectat: recv () şi WSARecv (). Acesta din urmă este, de asemenea, un apel Winsock 2. Un lucru important de reţinut este faptul că toate tampoanele asociate cu trimiterea şi primirea de date sunt de tip char care este doar byte simplu orientate spre date. În realitate, ea poate fi un tampon cu orice date brute in el, fie că e vorba de date binare sau de stringuri nu contează. În plus, codul de eroare returnat de către funcţiile send() si receive() este SOCKET_ERROR. Odată ce se returnează o eroare, apelam WSAGetLastError () pentru a obţine informaţii extinse despre eroare. Două dintre cele mai frecvente erori întâlnite sunt WSAECONNABORTED şi WSAECONNRESET. Ambele au legatura cu inchiderea conexiunii , fie printr-un timp de expirare sau prin închiderea conexiunii decatre client. O altă eroare frecventă este WSAEWOULDBLOCK, care este în mod normal, întâlnita atunci când, socluri nonblocking sau asincrone sunt folosite. Această eroare înseamnă în esenţă că funcţia specificată nu poate fi finalizata în acest moment.

172.16.11.201

send() and WSASend()

Prima funcţia API pentru a trimite date pe un socket conectat este :

int send(

SOCKET s, const char FAR * buf, int len,

int flags

); Parametrul SOCKET este folosit pentru a trimite datele .Al doilea parametru, buf, este un indicator de tampon de caractere care conţine datele care urmează să fie trimise. Al treilea parametru, len, precizează numărul de caractere în zona-tampon ce vor fi transmise . În cele din urmă, flag e un parametru care poate fi 0, MSG_DONTROUTE, sau MSG_OOB. MSG_DONTROUTE specifica ruta de transport pentru pachetele trimise. MSG_OOB semnifică faptul că datele ar trebui să fie trimise urgent. Send returnează numărul de octeţi trimis, în caz contrar, în cazul în care apare o eroare, SOCKET_ERROR va fi returnata. O eroare comună este WSAECO-NNABORTED, care apare atunci cand cade un circuit virtual din cauza unei erori de expirare sau de protocol. Atunci când se întâmplă acest lucru, socket-ar trebui să fie închise, deoarece nu mai este utilizabil. WSAECONNRESET se produce atunci când cererea de pe gazda de la distanţă reiniţializează un circuit virtual prin execuţia unui reset hard sau se încheie în mod neaşteptat, sau atunci când gazda de la distanţă este repornită. Din nou, socket-ar trebui să fie închis după ce această eroare se produce. Uultima eroare comună este WSAETIMEDOUT, care apare atunci când conexiunea este căzuta din cauza unei erori de reţea sistemului aflat la distanta . Functia WSASend (), este definita astfel:

a

int WSASend( SOCKET s, LPWSABUF lpBuffers, DWORD dwBufferCount, LPDWORD lpNumberOfBytesSent, DWORD dwFlags, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

); Socket este un descriptor valabil la o sesiune de conectare.Al doilea parametru este un indicator la una sau mai multe structuri WSABUF. Acest lucru poate fi fie o structură unică sau o serie de astfel de structuri. Al treilea parametru indică numărul de structuri WSABUF ce trebuie transmise. Amintiţi-vă că fiecare structură de WSABUF este un tampon . Am putea să ne întrebăm de ce-ar vrea sa trimita mai mult de un tampon de la un moment dat. Aceasta tehnica se numeşte scatter-gather I/O . LpNumberOfBytesSent este un pointer la o valoare DWORD care întoarce la un apel WSASend () contine numărul total de octeţi trimis. DwFlags e un parametru echivalent cu omologul său din send(). Ultimii doi parametri, lpOverlapped şi lpCompletionRoutine, sunt utilizati pentru overlapped I / O. WSASend () seteaza lpNumberOfBytesSent la numărul de octeţi trimisi. Funcţia returnează 0 în cazul succesului şi SOCKET_ERROR .

WSASendDisconnect()

int WSASendDisconnect(SOCKET s, LPWSABUF lpOutboundDisconnectData);

Out-of-Band Data

Atunci când o cerere pe un socket flux conectat are nevoie pentru a trimite date mai importante decât datele din flux, se pot marca datele importante ca out-of-band . Aplicatia de la celălalt capăt al conexiunii poate primi şi prelucra datele cu caracter OBD printr-un canal separat logic, care este conceptual independent de fluxul de date. In TCP, datele ODB sunt puse în aplicare prin intermediul unui bit 1 numit URG .

recv() and WSARecv()

int recv( SOCKET s, char FAR* buf, int len, int flags

); Primul parametru, S, este soclul pe care datele vor fi primite.Al doilea parametru, buf, este un tampon de caractere care va primi date, len este fie numărul de octeţi pe care doriţi să-i primiţi sau mărimea tampon , buf.

Stream Protocols

primi date, len este fie numărul de octeţi pe care doriţi să - i primiţi sau

Să presupunem că aveţi un tampon cu 2,048 octeţi de date pe care doriţi să le trimiteţi cu funcţiasend(). Codul pentru a trimite acest lucru este:

char sendbuff[2048];

int nBytes = 2048;

// Fill sendbuff with 2048 bytes of data

// Assume s is a valid, connected stream socket ret = send(s, sendbuff, nBytes, 0);

E posibil ca send() sa nu trimita toti cei 2048 de octeti datorita umplerii bufferelor sau datorita modificarii feresteri de receptie prin mecanismul terestrei glisante.Pentru a fi siguri ca trimitem toate caracterele din buffer putem folosi codul :

char sendbuff[2048];

int nBytes = 2048, nLeft, idx;

// Fill sendbuff with 2048 bytes of data nLeft = nBytes; idx = 0;

while (nLeft > 0)

{

// Assume s is a valid, connected stream socket ret = send(s, &sendbuff[idx], nLeft, 0); if (ret == SOCKET_ERROR)

{

// Error handler

}

nLeft -= ret;

idx += ret;

}

Breaking the Connection

Odată ce aţi terminat cu o conexiune, trebuie să închideţi soclul şi sa eliberati orice resurse asociate cu descriptorul de socket . Pentru a elibera de fapt, resursele asociate cu un soclu deschis,se utilizează closesocket () .

shutdown()

int shutdown(SOCKET s, int how);

Parametrul how poate fi SD_RECEIVE, SD_SEND, or SD_BOTH.

Value

Meaning

SD_SEND (0)

Shutdown send operations.

SD_RECEIVE (1)

Shutdown receive operations.

SD_BOTH (2)

Shutdown both send and receive operations.

closesocket()

int closesocket (SOCKET s);

TCP Receiver/Server

SD_BOTH (2) Shutdown both send and receive operations. closesocket() int closesocket (SOCKET s); TCP Receiver/Server
#include <winsock2.h> #include <stdio.h> // A sample of the select() return value int

#include <winsock2.h>

#include <stdio.h>

// A sample of the select() return value

int recvTimeOutTCP(SOCKET socket, long sec, long usec)

{

 

// Setup timeval variable struct timeval timeout;

struct fd_set fds;

// assign the second and microsecond variables timeout.tv_sec = sec; timeout.tv_usec = usec; // Setup fd_set structure FD_ZERO(&fds); FD_SET(socket, &fds); // Possible return values:

// -1: error occurred // 0: timed out // > 0: data ready to be read return select(0, &fds, 0, 0, &timeout);

}

int main(int argc, char **argv)

{

WSADATA

wsaData;

SOCKET

ListeningSocket, NewConnection;

SOCKADDR_IN

ServerAddr, SenderInfo;

int

Port = 7171;

// Receiving part

char

recvbuff[1024];

int

ByteReceived, i, nlen, SelectTiming;

// Initialize Winsock version 2.2 if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)

{

// The WSAGetLastError() function is one of the only functions // in the Winsock 2.2 DLL that can be called in the case of a WSAStartup failure printf("Server: WSAStartup failed with error %ld.\n", WSAGetLastError()); // Exit with error return 1;

}

else

{

printf("Server: The Winsock DLL found!\n"); printf("Server: The current status is %s.\n", wsaData.szSystemStatus);

}

if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2

)

{

//Tell the user that we could not find a usable WinSock DLL printf("Server: The dll do not support the Winsock version %u.%u!\n", LOBYTE(wsaData.wVersion),HIBYTE(wsaData.wVersion)); // Do the clean up WSACleanup(); // and exit with error return 1;

}

else

{

printf("Server: The dll supports the Winsock version %u.%u!\n", LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); printf("Server: The highest version this dll can support is

%u.%u\n", LOBYTE(wsaData.wHighVersion), HIBYTE(wsaData.wHighVersion));

}

// Create a new socket to listen for client connections. ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

// Check for errors to ensure that the socket is a valid socket. if (ListeningSocket == INVALID_SOCKET)

{

printf("Server: Error at socket(), error code: %ld.\n", WSAGetLastError()); // Clean up WSACleanup(); // and exit with error return 1;

}

else printf("Server: socket() is OK!\n");

// Set up a SOCKADDR_IN structure that will tell bind that we

// want to listen for connections on all interfaces using port

7171.

// The IPv4 family ServerAddr.sin_family = AF_INET; // host-to-network byte order ServerAddr.sin_port = htons(Port); // Listen on all interface, host-to-network byte order ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);

// Associate the address information with the socket using bind. // Call the bind function, passing the created socket and the sockaddr_in // structure as parameters. Check for general errors. if (bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)

{

printf("Server: bind() failed! Error code: %ld.\n", WSAGetLastError()); // Close the socket closesocket(ListeningSocket); // Do the clean up WSACleanup(); // and exit with error return 1;

}

else printf("Server: bind() is OK!\n");

// Listen for client connections with a backlog of 5 if (listen(ListeningSocket, 5) == SOCKET_ERROR)

{

printf("Server: listen(): Error listening on socket %ld.\n", WSAGetLastError()); // Close the socket closesocket(ListeningSocket); // Do the clean up WSACleanup(); // Exit with error return 1;

}

else

printf("Server: listen() is OK, I'm listening for

connections

\n");

// Set 10 seconds 10 useconds timeout SelectTiming = recvTimeOutTCP(ListeningSocket, 10, 10);

switch (SelectTiming)

{

case 0:

// Timed out, do whatever you want to handle this situation printf("\nServer: Timeout lor while waiting you retard

client!

\n");

break; case -1:

// Error occurred, more tweaking here and the recvTimeOutTCP() printf("\nServer: Some error encountered with code number:

%ld\n", WSAGetLastError()); break;

default:

{

// Accept a new connection when available. 'while' always

true

while(1)

{

// Reset the NewConnection socket to SOCKET_ERROR // Take note that the NewConnection socket in not listening NewConnection = SOCKET_ERROR; // While the NewConnection socket equal to SOCKET_ERROR

// which is always true in this case

while(NewConnection == SOCKET_ERROR)

{

// Accept connection on the ListeningSocket socket and

assign

// it to the NewConnection socket, let the ListeningSocket // do the listening for more connection NewConnection = accept(ListeningSocket, NULL, NULL);

printf("\nServer: accept() is OK

printf("Server: New client got connected, ready to

\n");

receive and send data

\n");

// At this point you can do two things with these sockets // 1. Wait for more connections by calling accept again

//

// 2. Start sending or receiving data on NewConnection.

on ListeningSocket (loop)

ByteReceived = recv(NewConnection, recvbuff, sizeof(recvbuff), 0);

// When there is data

if ( ByteReceived > 0 )

{

printf("Server: recv() looks fine

\n");

// Some info on the receiver side getsockname(ListeningSocket, (SOCKADDR *)&ServerAddr, (int *)sizeof(ServerAddr)); printf("Server: Receiving IP(s) used: %s\n", inet_ntoa(ServerAddr.sin_addr)); printf("Server: Receiving port used: %d\n", htons(ServerAddr.sin_port));

// Some info on the sender side // Allocate the required resources memset(&SenderInfo, 0, sizeof(SenderInfo)); nlen = sizeof(SenderInfo);

getpeername(NewConnection, (SOCKADDR *)&SenderInfo,

&nlen);

printf("Server: Sending IP used: %s\n", inet_ntoa(SenderInfo.sin_addr)); printf("Server: Sending port used: %d\n", htons(SenderInfo.sin_port));

// Print the received bytes. Take note that this is the

total

// byte received, it is not the size of the declared

buffer

printf("Server: Bytes received: %d\n", ByteReceived); // Print what those bytes represent printf("Server: Those bytes are: \""); // Print the string only, discard other // remaining 'rubbish' in the 1024 buffer size for(i=0;i < ByteReceived;i++) printf("%c", recvbuff[i]); printf("\"");

}

// No data else if ( ByteReceived == 0 ) printf("Server: Connection closed!\n"); // Others

else printf("Server: recv() failed with error code: %d\n", WSAGetLastError());

}

// Clean up all the send/recv communication, get ready for

new one

if( shutdown(NewConnection, SD_SEND) != 0) printf("\nServer: Well, there is something wrong with the shutdown(). The error code: %ld\n", WSAGetLastError()); else

printf("\nServer: shutdown() looks OK

\n");

// Well, if there is no more connection in 15 seconds, // just exit this listening loop if( recvTimeOutTCP(ListeningSocket, 15, 0) == 0) break;

}

}

}

printf("\nServer: The listening socket is timeout

\n");

// When all the data communication and listening finished, close the socket

if(closesocket(ListeningSocket) != 0) printf("Server: Cannot close \"ListeningSocket\" socket. Error code: %ld\n", WSAGetLastError()); else

printf("Server: Closing \"ListeningSocket\" socket

\n");

// Finally and optionally, clean up all those WSA setup

if(WSACleanup() != 0) printf("Server: WSACleanup() failed! Error code: %ld\n", WSAGetLastError()); else

printf("Server: WSACleanup() is OK

return 0;

}

\n");

failed! Error code: %ld\n" , WSAGetLastError()); else printf( "Server: WSACleanup() is OK return 0; } \n"