ФЕДЕРАЛЬНОЕ АГЕНСТВО ПО ОБРАЗОВАНИЮ РФ ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ПРОФЕССИАНАЛЬНОГО ОБРАЗОВАНИЯ МОСКОВСКИЙ ГОСУДАРСТВЕННЫЙ ИНСТИТУТ РАДИОТЕХНИКИ, ЭЛЕКТРОНИКИ И АВТОМАТИКИ (ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ) Факультет Кибернетики Кафедра Интеллектуальных технологий и систем Лабораторная работа №6 Дисциплина: Сетевые технологии Тема: «Параллельный сервер ТСР» Студент: Ижбулатов Р.М. Группа ИИ-1-03 Руководитель: Торхова Н.А. Москва 2007. Задание 1. Разработать параллельный эхо – сервер Его функции: - после установки соединения с каждым клиентом, порождая новый процесс thread в котором происходит взаимодействие с этими клиентами; - createthread под Windows; - fork под Unix; - сервер получает данные от клиента и отсылает их в неизмененном виде клиенту. 2. Реализовать текстовый клиент Его функции: - клиент считывает строку текста из стандартного потока ввода и отсылает ее серверу; - клиент считывает отраженную строку из сети и выводит ее в стандартный поток вывода. 3. С помощью реализованного эхо сервера и клиента, исследовать: - что происходит с клиентом, если процесс сервера завешен до завершения клиента; - что происходит с сервером, если процесс клиента завершен некорректно до завершения сервера. 1. Echo-сервер #if defined(WIN32) #include <WinSock2.h> #pragma comment(lib,"ws2_32.lib") #endif #include <stdio.h> #if defined(WIN32) DWORD WINAPI ThreadProc(LPVOID lpParameter); #endif int main(int argc, char **argv) { if (argc < 3) { printf("echo server v0.1\nusage: echo_server.exe <bindaddress> <bindport>\n"); return 0; } #if defined(WIN32) WSADATA wsaData; WORD wVersionRequested = MAKEWORD( 2, 2 ); int err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { printf("can't find usable wsock32.dll\n"); return 1; } if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { printf("wsock32.dll of wrong version\n"); WSACleanup(); return 2; } #endif SOCKET listener = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if (listener == 0) { printf("failed to create socket\n"); return 3; } sockaddr_in listener_addr; listener_addr.sin_family = AF_INET; listener_addr.sin_addr.S_un.S_addr = inet_addr(argv[1]); listener_addr.sin_port = htons(atoi(argv[2])); int ret = bind(listener,(const sockaddr*)&listener_addr,sizeof(listener_addr)); if (ret != 0) { printf("failed to bind socket\n"); return 4; } ret = listen(listener,SOMAXCONN); if (ret != 0) { printf("failed to put socket into listening state\n"); return 5; } while (1) { sockaddr_in client_addr; int sizeofclient_addr = sizeof(client_addr); SOCKET *client_sock = (SOCKET *)malloc(sizeof(SOCKET)); *client_sock = accept(listener,(sockaddr*)&client_addr,&sizeofclient_addr); if (*client_sock != INVALID_SOCKET) { #if defined(WIN32) CreateThread(NULL,0,ThreadProc,(LPVOID)client_sock,0,NULL); #endif } else { printf("failed to accept the connection\n"); } } return 0; } #if defined(WIN32) DWORD WINAPI ThreadProc(LPVOID lpParameter) { SOCKET *s = (SOCKET*)lpParameter; char buf[1024]; memset(buf,0,1024); int rlen,slen; printf("client thread (socket %d) is ready\n",*s); while (1) { rlen = recv(*s,buf,1024,0); printf("%10d %10d> %s\n",rlen,*s,buf); if (rlen == 0) { printf("socket %d closed the connection\n",*s); closesocket(*s); break; } else if (rlen == SOCKET_ERROR) { printf("recv socket error at socket %d\n",*s); closesocket(*s); break; } slen = send(*s,buf,rlen,0); printf("%10d %10d< %s\n",rlen,*s,buf); if (rlen == SOCKET_ERROR) { printf("send socket error at socket %d\n",*s); closesocket(*s); break; } memset(buf,0,rlen); } return 0; } #endif 2. Echo-клент #if defined(WIN32) #include <WinSock2.h> #pragma comment(lib,"ws2_32.lib") #endif #include <stdio.h> int main(int argc, char **argv) { if (argc < 3) { printf("echo client v0.1\nusage: echo_client.exe <servaddress> <servport>\n"); return 0; } #if defined(WIN32) WSADATA wsaData; WORD wVersionRequested = MAKEWORD( 2, 2 ); int err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { printf("can't find usable wsock32.dll\n"); return 1; } if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) { printf("wsock32.dll of wrong version\n"); WSACleanup(); return 2; } #endif SOCKET client = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if (client == 0) { printf("failed to create socket\n"); return 3; } sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_addr.S_un.S_addr = inet_addr(argv[1]); server_addr.sin_port = htons(atoi(argv[2])); int ret = connect(client,(const sockaddr*)&server_addr,sizeof(server_addr)); if (ret != 0) { printf("failed to connect to %s:%s\n",argv[1],argv[2]); return 5; } char buf[1024]; int slen,rlen; while (1) { scanf("%s",buf); int blen = strlen(buf); if (strcmp(buf,"QUIT") == 0) { printf("QUIT input from user, closing socket...\n"); closesocket(client); break; } slen = send(client,buf,blen,0); printf("%10d< %s\n",slen,buf); if (slen == SOCKET_ERROR) { printf("server socket error\n"); break; } rlen = recv(client,buf,1024,0); printf("%10d>%s\n",rlen,buf); if (rlen == 0) { printf("server closed the connection\n"); break; } else if (rlen == SOCKET_ERROR) { printf("server socket error\n"); break; } } closesocket(client); return 0; } 3. Исследование работы сервера и клиента На рисунке показан вывод сервера в результате следующей цепочки событий: Сервер слушает. Клиент 1 соединяется. Сервер принимает соединение, создаёт тред. Сервер продолжает слушать. Клиент 2 соединяется. Сервер принимает соединение, создаёт тред. Сервер продолжает слушать. Клиент 1 посылает данные. Сервер отсылает их обратно клиенту. Клиент 2 посылает данные. Сервер отсылает их обратно клиенту. Клиент 1 умирает. Сервер получает данные. Сервер отсылает данные обратно клиенту. Сервер получает ошибку сокета. Сервер закрывает сокет и тред. Клиент 2 закрывает сокет. Сервер закрывает сокет и тред. Для аномалии (сервер получает строку «OWRLD.» ещё один раз, после того, как Клиент 2 умер) внятного объяснения найдено не было. На рисунке показан вывод клиента в результате следующей цепочки событий: Сервер слушает. Клиент соединяется. Сервер принимает соединение, создаёт тред. Клиент посылает данные. Сервер отсылает их обратно клиенту. Сервер умирает. Клиент посылает данные. Сокет клиента сообщает об ошибке. Клиент закрывает сокет и закрывается.