Тема: Взаимодействие процессов через механизм гнезд

 

Лабораторная работа №

Тема: Взаимодействие процессов через механизм гнезд.

Цель : Научиться передавать данные и сообщения между процессами компьютеров глобальной сети.

 

Содержание отчета:

  1. Тема.
  2. Цель.
  3. Индивидуальное задание.
  4. Описание используемых функций и структур.
  5. Тексты программ.
  6. Результат работы.
  7. Выводы.

 

Теоретические сведения.

Winsock.

Winsock – это Windows API нижнего уровня для программирования TCP/IP, основанный на Berkley Sockets API для UNIX. Часть кода находится в wsock32.dll (в том числе экспортируемые функции, вызываемые вашей программой), а часть – в ядре Windows. При помощи этого – API можно писать как клиентское, так и серверное программное обеспечение. Новая и более сложная версия Winsock 2 включена в составWindows NT 4.0.

 

Классы Winsock в MFC.

Классы CAsyncSocket и CSocket не годятся для 32-разрядного синхронного программирования. В интерактивной справочной системе по Visual C++ говорится, что CSocket можно использовать для синхронного программирования, но если заглянуть в исходные тексты, то можно увидеть безобразный код на основе сообщений, оставшийся от Win16.

 

Невозможно написать хорошую программу с использованием Winsock, не разобравшись с понятием сокета (socket), используемого для передачи и приема пакетов данных по сети. Чтобы полностью понять сокеты, необходимо хорошо знать нижележащие протоколы интернета.

 

Протокол IP.

  Рис.1 Упрощенный формат IP-датаграммы

Протокол UDP.

На рис.2 видно, что в заголовке UDP передается дополнительная информация – номера портов источника и получателя. Эти номера используются…  

Протокол ТСР.

Протокол ТСР устанавливает между двумя компьютерами дуплексное соединение типа «точка-точка». Прогаммы на каждом конце соединения используют… После установления соединения каждая программа может посылать другой программе… Номера последовательности программы-отправителя являются не индексами сегментов, а индексами в потоке байтов.…

Основы HTTP.

HTTP построен на основе ТСР. Он работает следующим образом: сервер ожидает (listens) запросы на порт 80; клиент (обычно браузер) соединяется…   GET/customers/newproducts.html HTTP/1.0

Упрощенный сервер НТТР

Код действительно работает с браузером. Сервер всего лишь возвращает несколько жестко зашитых в него заголовков и НТМL-операторов в ответ на любой запрос GET.

 

Инициализация Winsock

Перед вызовом любой Winsock-функции необходимо инициализировать Winsock-библиотеку. Два следующих оператора в функции-члене InitInstance делают это:

 

WSADATA wsd;

WSAStartup(0x0101, &wsd);

 

Запуск сервера

  CBlockingSocket g_sListen; // один глобальный сокет для ожидания запросов Void CSocketView::OnInternetStartServer(){

Серверный поток

  UINT ServerThreadProc(LPVOID pParam) {CSockAddr saClient;

Очистка

  try{ g_sListen.Close();

Упрощенный клиент НТТР

  GET / HTTP/1.0  

Приложение А

Blocksock.h

// needs winsock.h in the precompiled headers typedef const struct sockaddr* LPCSOCKADDR; class CBlockingSocketException : public CException

Приложение Б

blocksock.cpp (CBlockingSocketException, CBlockingSocket, CHttpBlockingSocket)

#include <stdafx.h>

#include "blocksock.h"

 

// Class CBlockingSocketException

IMPLEMENT_DYNAMIC(CBlockingSocketException, CException)

 

CBlockingSocketException::CBlockingSocketException(char* pchMessage)

{

m_strMessage = pchMessage;

m_nError = WSAGetLastError();

}

 

BOOL CBlockingSocketException::GetErrorMessage(LPTSTR lpstrError, UINT nMaxError,

PUINT pnHelpContext /*= NULL*/)

{

 

char text[200];

if(m_nError == 0) {

wsprintf(text, "%s error", (const char*) m_strMessage);

}

else {

wsprintf(text, "%s error #%d", (const char*) m_strMessage, m_nError);

}

strncpy(lpstrError, text, nMaxError - 1);

return TRUE;

}

 

// Class CBlockingSocket

IMPLEMENT_DYNAMIC(CBlockingSocket, CObject)

 

void CBlockingSocket::Cleanup()

{

// doesn't throw an exception because it's called in a catch block

if(m_hSocket == NULL) return;

VERIFY(closesocket(m_hSocket) != SOCKET_ERROR);

m_hSocket = NULL;

}

 

void CBlockingSocket::Create(int nType /* = SOCK_STREAM */)

{

ASSERT(m_hSocket == NULL);

if((m_hSocket = socket(AF_INET, nType, 0)) == INVALID_SOCKET) {

throw new CBlockingSocketException("Create");

}

}

 

void CBlockingSocket::Bind(LPCSOCKADDR psa)

{

ASSERT(m_hSocket != NULL);

if(bind(m_hSocket, psa, sizeof(SOCKADDR)) == SOCKET_ERROR) {

throw new CBlockingSocketException("Bind");

}

}

 

void CBlockingSocket::Listen()

{

ASSERT(m_hSocket != NULL);

if(listen(m_hSocket, 5) == SOCKET_ERROR) {

throw new CBlockingSocketException("Listen");

}

}

 

BOOL CBlockingSocket::Accept(CBlockingSocket& sConnect, LPSOCKADDR psa)

{

ASSERT(m_hSocket != NULL);

ASSERT(sConnect.m_hSocket == NULL);

int nLengthAddr = sizeof(SOCKADDR);

sConnect.m_hSocket = accept(m_hSocket, psa, &nLengthAddr);

if(sConnect == INVALID_SOCKET) {

// no exception if the listen was canceled

if(WSAGetLastError() != WSAEINTR) {

throw new CBlockingSocketException("Accept");

}

return FALSE;

}

return TRUE;

}

 

void CBlockingSocket::Close()

{

if (NULL == m_hSocket)

return;

 

if(closesocket(m_hSocket) == SOCKET_ERROR) {

// should be OK to close if closed already

throw new CBlockingSocketException("Close");

}

m_hSocket = NULL;

}

 

void CBlockingSocket::Connect(LPCSOCKADDR psa)

{

ASSERT(m_hSocket != NULL);

// should timeout by itself

if(connect(m_hSocket, psa, sizeof(SOCKADDR)) == SOCKET_ERROR) {

throw new CBlockingSocketException("Connect");

}

}

 

int CBlockingSocket::Write(const char* pch, const int nSize, const int nSecs)

{

int nBytesSent = 0;

int nBytesThisTime;

const char* pch1 = pch;

do {

nBytesThisTime = Send(pch1, nSize - nBytesSent, nSecs);

nBytesSent += nBytesThisTime;

pch1 += nBytesThisTime;

} while(nBytesSent < nSize);

return nBytesSent;

}

 

int CBlockingSocket::Send(const char* pch, const int nSize, const int nSecs)

{

ASSERT(m_hSocket != NULL);

// returned value will be less than nSize if client cancels the reading

FD_SET fd = {1, m_hSocket};

TIMEVAL tv = {nSecs, 0};

if(select(0, NULL, &fd, NULL, &tv) == 0) {

throw new CBlockingSocketException("Send timeout");

}

int nBytesSent;

if((nBytesSent = send(m_hSocket, pch, nSize, 0)) == SOCKET_ERROR) {

throw new CBlockingSocketException("Send");

}

return nBytesSent;

}

 

int CBlockingSocket::Receive(char* pch, const int nSize, const int nSecs)

{

ASSERT(m_hSocket != NULL);

FD_SET fd = {1, m_hSocket};

TIMEVAL tv = {nSecs, 0};

if(select(0, &fd, NULL, NULL, &tv) == 0) {

throw new CBlockingSocketException("Receive timeout");

}

 

int nBytesReceived;

if((nBytesReceived = recv(m_hSocket, pch, nSize, 0)) == SOCKET_ERROR) {

throw new CBlockingSocketException("Receive");

}

return nBytesReceived;

}

 

int CBlockingSocket::ReceiveDatagram(char* pch, const int nSize, LPSOCKADDR psa, const int nSecs)

{

ASSERT(m_hSocket != NULL);

FD_SET fd = {1, m_hSocket};

TIMEVAL tv = {nSecs, 0};

if(select(0, &fd, NULL, NULL, &tv) == 0) {

throw new CBlockingSocketException("Receive timeout");

}

 

// input buffer should be big enough for the entire datagram

int nFromSize = sizeof(SOCKADDR);

int nBytesReceived = recvfrom(m_hSocket, pch, nSize, 0, psa, &nFromSize);

if(nBytesReceived == SOCKET_ERROR) {

throw new CBlockingSocketException("ReceiveDatagram");

}

return nBytesReceived;

}

 

int CBlockingSocket::SendDatagram(const char* pch, const int nSize, LPCSOCKADDR psa, const int nSecs)

{

ASSERT(m_hSocket != NULL);

FD_SET fd = {1, m_hSocket};

TIMEVAL tv = {nSecs, 0};

if(select(0, NULL, &fd, NULL, &tv) == 0) {

throw new CBlockingSocketException("Send timeout");

}

 

int nBytesSent = sendto(m_hSocket, pch, nSize, 0, psa, sizeof(SOCKADDR));

if(nBytesSent == SOCKET_ERROR) {

throw new CBlockingSocketException("SendDatagram");

}

return nBytesSent;

}

 

void CBlockingSocket::GetPeerAddr(LPSOCKADDR psa)

{

ASSERT(m_hSocket != NULL);

// gets the address of the socket at the other end

int nLengthAddr = sizeof(SOCKADDR);

if(getpeername(m_hSocket, psa, &nLengthAddr) == SOCKET_ERROR) {

throw new CBlockingSocketException("GetPeerName");

}

}

 

void CBlockingSocket::GetSockAddr(LPSOCKADDR psa)

{

ASSERT(m_hSocket != NULL);

// gets the address of the socket at this end

int nLengthAddr = sizeof(SOCKADDR);

if(getsockname(m_hSocket, psa, &nLengthAddr) == SOCKET_ERROR) {

throw new CBlockingSocketException("GetSockName");

}

}

 

//static

CSockAddr CBlockingSocket::GetHostByName(const char* pchName, const USHORT ushPort /* = 0 */)

{

hostent* pHostEnt = gethostbyname(pchName);

if(pHostEnt == NULL) {

throw new CBlockingSocketException("GetHostByName");

}

ULONG* pulAddr = (ULONG*) pHostEnt->h_addr_list[0];

SOCKADDR_IN sockTemp;

sockTemp.sin_family = AF_INET;

sockTemp.sin_port = htons(ushPort);

sockTemp.sin_addr.s_addr = *pulAddr; // address is already in network byte order

return sockTemp;

}

 

//static

const char* CBlockingSocket::GetHostByAddr(LPCSOCKADDR psa)

{

hostent* pHostEnt = gethostbyaddr((char*) &((LPSOCKADDR_IN) psa)

->sin_addr.s_addr, 4, PF_INET);

if(pHostEnt == NULL) {

throw new CBlockingSocketException("GetHostByAddr");

}

return pHostEnt->h_name; // caller shouldn't delete this memory

}

 

// Class CHttpBlockingSocket

IMPLEMENT_DYNAMIC(CHttpBlockingSocket, CBlockingSocket)

 

CHttpBlockingSocket::CHttpBlockingSocket()

{

m_pReadBuf = new char[nSizeRecv];

m_nReadBuf = 0;

}

 

CHttpBlockingSocket::~CHttpBlockingSocket()

{

delete [] m_pReadBuf;

}

 

int CHttpBlockingSocket::ReadHttpHeaderLine(char* pch, const int nSize, const int nSecs)

// reads an entire header line through CRLF (or socket close)

// inserts zero string terminator, object maintains a buffer

{

int nBytesThisTime = m_nReadBuf;

int nLineLength = 0;

char* pch1 = m_pReadBuf;

char* pch2;

do {

// look for lf (assume preceded by cr)

if((pch2 = (char*) memchr(pch1 , 'n', nBytesThisTime)) != NULL) {

ASSERT((pch2) > m_pReadBuf);

ASSERT(*(pch2 - 1) == 'r');

nLineLength = (pch2 - m_pReadBuf) + 1;

if(nLineLength >= nSize) nLineLength = nSize - 1;

memcpy(pch, m_pReadBuf, nLineLength); // copy the line to caller

m_nReadBuf -= nLineLength;

memmove(m_pReadBuf, pch2 + 1, m_nReadBuf); // shift remaining characters left

break;

}

pch1 += nBytesThisTime;

nBytesThisTime = Receive(m_pReadBuf + m_nReadBuf, nSizeRecv - m_nReadBuf, nSecs);

if(nBytesThisTime <= 0) { // sender closed socket or line longer than buffer

throw new CBlockingSocketException("ReadHeaderLine");

}

m_nReadBuf += nBytesThisTime;

}

while(TRUE);

*(pch + nLineLength) = '';

return nLineLength;

}

 

int CHttpBlockingSocket::ReadHttpResponse(char* pch, const int nSize, const int nSecs)

// reads remainder of a transmission through buffer full or socket close

// (assume headers have been read already)

{

int nBytesToRead, nBytesThisTime, nBytesRead = 0;

if(m_nReadBuf > 0) { // copy anything already in the recv buffer

memcpy(pch, m_pReadBuf, m_nReadBuf);

pch += m_nReadBuf;

nBytesRead = m_nReadBuf;

m_nReadBuf = 0;

}

do { // now pass the rest of the data directly to the caller

nBytesToRead = min(nSizeRecv, nSize - nBytesRead);

nBytesThisTime = Receive(pch, nBytesToRead, nSecs);

if(nBytesThisTime <= 0) break; // sender closed the socket

pch += nBytesThisTime;

nBytesRead += nBytesThisTime;

}

while(nBytesRead <= nSize);

return nBytesRead;

}