- •3.2. Протокол доставки пользовательских дейтаграмм udp
- •5. Рабочее задание и указание к его выполнению
- •Цель работы
- •Подготовка к работе
- •Стек протоколов tcp/ip
- •3.1. Протокол межсетевого взаимодействия ip, формат ip пакета.
- •3.2. Протокол доставки пользовательских дейтаграмм udp.
- •3.3. Протокол надежной доставки сообщений tcp
- •Программирование по схеме “клиент-сервер” с использованием интерфейса Windows Sockets
- •Int wsaStartup(word wVersionRequested, lpwsadata lpWsaData);
- •Int wsaCleanup(void);
- •Int wsaGetLastError(void);
- •Socket socket(int af, int type, int protocol);
- •Int closesocket(socket sock);
- •Char far * inet_ntoa(struct in_addr in);
- •#Define h_addr h_addr_list[0]
- •Int bind(socket s, const struct sockaddr far *name, int namelen);
- •Int listen(socket sock, int backlog);
- •Socket accept(socket s, struct sockaddr far *addr, int far * addrlen);
- •Void ExitThread(uint fuExitCode);
- •Bool TerminateThread(handle hThread, dword dwExitCode);
- •Connect (socket s, const struct sockaddr *peer, int peer_len);
- •Передача и приём данных.
- •Int sendto(socket s,const void* buf, size_t len, int flags,
- •Int recvfrom(socket s,void* buf,size_t len, int flags,
- •Алгоритм построения клиента и сервера.
- •5. Рабочее задание и указание к его выполнению.
- •Содержание отчёта.
- •Вопросы для самопроверки.
- •8. Библиографический список
Int sendto(socket s,const void* buf, size_t len, int flags,
struct sockaddr *to, int tolen);
Int recvfrom(socket s,void* buf,size_t len, int flags,
struct sockaddr *from, int* fromlen);
Пример использования функций обмена данными:
char s[255]; char Buf[300]; char addr[15]; #define PORT 4554 ………………………. DWORD WINAPI Reciever(LPVOID) { // Обработчик принимаемых данных(строк максимальной длины //255) SOCKADDR_IN sFrom; int len = sizeof(sFrom); while(1) { int rv = recvfrom(serv_sock,s,255,0,(PSOCKADDR)&sFrom,&len); if (rv == SOCKET_ERROR); // данных нет else Buf = inet_ntoa(sFrom.sin_addr))+">>"+s; } } //--------------------------------------------------------- void Sender(char Key) { if (Key == '\r' ){ SOCKADDR_IN serv_ad1; serv_ad1.sin_family = AF_INET; serv_ad1.sin_port = htons(PORT); serv_ad1.sin_addr.s_addr = inet_addr(addr); sendto(serv_sock,s,sizeof(s),0, (LPSOCKADDR)&serv_ad1, sizeof(serv_ad1)); } } |
Алгоритм построения клиента и сервера.
Каркас простого TCP-сервера с одним соединением, работающим на прием данных.
#include "winsock2.h"
DWORD WINAPI Connection(LPVOID); DWORD WINAPI Reciever(LPVOID); SOCKADDR_IN serv_ad; SOCKADDR_IN cl_ad; SOCKET serv_sock; char s[255]; HANDLE hThread[2]; DWORD WINAPI Connection(LPVOID) {
int len = sizeof(cl_ad);
do srv_sock = accept(serv_sock,(LPSOCKADDR)&cl_ad, (int FAR *)&len);
while (srv_sock == INVALID_SOCKET);
hThread[1] = CreateThread(NULL,0,Reciever,NULL,0,NULL); MessageDlg("Connect OK", mtInformation, TMsgDlgButtons() << mbOK, 0); int code; ExitThread(code); // Если соединений будет больше то //завершать поток не надо
} //--------------------------------------------------------- DWORD WINAPI Reciever(LPVOID) { while(1) { int rv = recv(srv_sock,s,255,0); // Теперь принятые //данные в буфере s.
if (rv == SOCKET_ERROR) MessageDlg(IntToStr(rv), mtError, TMsgDlgButtons() << mbOK, 0); else ; // здесь вы обрабатываете поступившие данные и //управляете потоком приема } } int WinMain ( void ) {
if (WSAStartup(MAKEWORD(2,0),&LPWS)!=0) { MessageDlg(IntToStr(rs), mtError, TMsgDlgButtons() << mbOK, 0); serv_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if (serv_sock ==INVALID_SOCKET) MessageDlg(“Error Sock”, mtError,TMsgDlgButtons() << mbOK, 0); serv_ad.sin_family = AF_INET; serv_ad.sin_port = htons(PORT); serv_ad.sin_addr.s_addr = INADDR_ANY; if (bind(serv_sock, (PSOCKADDR)&serv_ad, sizeof(serv_ad)) == SOCKET_ERROR) MessageDlg(IntToStr(svz), mtError,TMsgDlgButtons() << mbOK, 0); int tst = listen(serv_sock,5); hThread[0]=CreateThread(NULL,0,Connection, NULL,0,NULL);
} |
Каркас простого TCP-клиента с одним соединением, работающим на передачу данных, использующих асинхронную передачу данных.
#define WM_SOCKET WM_USER+1 ……………………… // инициализация сокета и перевод его в асинхронный режим // приема передачи данных void __fastcall TForm1::FormCreate(TObject *Sender) { // Создание сервера // Создаем сокет serv_sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); if (serv_sock ==INVALID_SOCKET) MessageDlg(IntToStr(serv_sock), mtError, TMsgDlgButtons() << mbOK, 0); // Принадлежность адреса к семейству интернет адресов serv_ad.sin_family = AF_INET; // Указываем порт serv_ad.sin_port = htons(4554); // Тип адреса любой serv_ad.sin_addr.s_addr = INADDR_ANY; // Связываем сокет с адресом if (bind(serv_sock, (PSOCKADDR)&serv_ad, sizeof(serv_ad))==SOCKET_ERROR) MessageDlg("Ошибка привязки структуры к сокету!!!", mtError, TMsgDlgButtons() << mbOK, 0); // Задаем режим передачи – асинхронный и указываем в //качестве функции обработчика сообщений - RecieveData WSAAsyncSelect(serv_sock, static_cast<HWND>(Form1->Handle), WM_SOCKET,FD_ACCEPT | FD_READ | FD_WRITE); Application->OnMessage = RecieveData; } …………………………………… void __fastcall TForm1::RecieveData(TMsg & Msg, bool &handled) // как только пришло сообщение о работе с сокетом, // обрабатываем его { if (Msg.message == WM_SOCKET) { switch (WSAGETSELECTEVENT(Msg.lParam)) { case FD_READ: { // Прием данных из сокета int rv=recvfrom(serv_sock,s,255,0, (PSOCKADDR)&sFrom,&len); if (rv == SOCKET_ERROR) { // Если пришел пустой пакет значит конец // файла, закрываем файл } else { if (FileFull) { s[rv] = 0; FileName=CreateFile(s,GENERIC_WRITE, FILE_SHARE_WRITE,NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL); FileFull = false; } else { DWORD* lBlock = new DWORD; if (0==rv) { if (!FileFull) { CloseHandle(FileName); FileFull = true; break; } } WriteFile(FileName,s,rv,lBlock,NULL); } } } default: // TO DO } } |
Каркас простого TCP-клиента с одним соединением, работающим на передачу данных.
#include "winsock2.h"
int Sender(char Buf[4]); SOCKADDR_IN dest_sin; SOCKET clnt_sock; //--------------------------------------------------------- int Sender(char Buf[4]) { int Rez = send(clnt_sock,Buf,4,0); if (Rez == SOCKET_ERROR) return -1; return 0; }
int WinMain ( void ) { PHOSTENT phe; if (WSAStartup(MAKEWORD(2,0),&LPWS)!=0) { MessageDlg(IntToStr(rs), mtError, TMsgDlgButtons() << mbOK, 0); clnt_sock = socket(AF_INET, SOCK_STREAM, 0); dest_sin.sin_family = AF_INET; phe = gethostbyname(“server”); memcpy((char FAR *)&(dest_sin.sin_addr), phe->h_addr, phe->h_length); dest_sin.sin_port = htons(1024); if (connect(clnt_sock, (PSOCKADDR)&dest_sin, sizeof(dest_sin))== SOCKET_ERROR) MessageDlg("Connect Failed", mtError, TMsgDlgButtons() << mbOK, 0); Sender(“TEST”); } |
Каркас UDP-сервера в основном похож на каркас TCP-сервера. Его отличительная особенность – не нужно устанавливать опция сокета SO_REUSEADDR и обращаться к системным вызовам accept и listen, поскольку UDP – это пртокол, не требующий логического соединения.
При использовании протокола негарантированной доставки UDP необходимо создать сокет, осуществить его привязку к адресу с помощью функции bind и начинать прием или передачу данных с помощью описанных выше функций recvfrom и sendto. Структура соединения по протоколу UDP приведена ниже.
