Курсовая: Сетевые крестики нолики

                                
     ЧЕЛЯБИНСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
                                      ОТЧЕТ                                      
                 о практической работе по дисциплине лСети ЭВМ                 
Факультет: математический                 Выполнила: Насибулина
Раиса Сулеймановна
Специальность: прикладная                  Группа: МП-402
     математика
Кафедра:  системного                              Проверил: Соколов
Андрей Анатольевич
     программирования
                                 Челябинск 2004                                 
                                                                              
     
                                Содержание                                
                    1. Задание. 3                    
     2. Используемые сетевые сообщения. 4
     3. Основные функции программы. 5
     4. Процессы. 7
     Диаграммы: 8
     5. Текст программы. 100
     

1. Задание.

Разработать приложения клиент и сервер для сетевой игры лКрестики ноликис использованием UDP протокола. Клиент лактивный, сервер лпассивный.

2. Используемые сетевые сообщения.

Каждый отправляемый программой пакет имеет идентификатор вида пакета и само сообщение. В программе использованы следующие 5 видов пакетов: 1) пакет линформация о карте. В его сообщении содержатся карта игры. При ходе одной стороны передается новая карта и номер хода /Info (i)/. 2) пакет лподтверждение хода. Сообщение содержит те же данные, что и в 1). отправляется при изменении одной координаты /Info (r)/. 3) пакет лустановка и проверка соединения Установка соединения с сервером и его проверка /Connect/. 4) пакет ложидание, соединение и проверка. Ожидание соединения, при соединении периодическая проверка соединения /Ping (r)/. 5) пакет лзапрос на получение информации от сервера /Info (r)/. Пакеты 1). Ц 5). имеют следующий формат. union packd { char buff[63]; // buffer struct pack p; // данные }; где struct pack{ char action, step; // тип пакета, номер шага char matr[4][4]; // карта } ar;

3. Основные функции программы.

 void initme(){; //Инициализация клиентского и серверного сокетов.  void ServerStop(HWND hWnd) //Освобождение ресурсов, завершение нитей, закрытие сокетов.  bool clientconnect(HWND hWnd) //Создание нити соединения клиента с сервером.  void startserv(HWND hWnd) //Запуск сервера. Запуск нити для работы с клиентом, проверка наличия связи с клиентом.  void putplus(HWND hWnd, char flag,char i, char j) //Принятие хода клиента, отправка карты серверу (или клиенту). //Параметр flag имеет следующие значения: // PLUS Ц крестик, ZERO Ц нолик. // (i,j) Ц координаты хода.  DWORD WINAPI ThreadAction_cl(LPVOID param)  DWORD WINAPI ThreadAction_serv(LPVOID param) //нити клиента и сервера. Проверяют наличие друг друга (клиента и сервера) При получении информации о ходе Ц обновляется карта и посылается подтверждение.  DWORD WINAPI ThreadAction_put(LPVOID param) //Нить для отправки информации о ходе сопернику. Производит периодическую отправку информации о ходе до получения подтверждения о ходе.  void drawme(HWND hWnd) //перерисовка карты в случае хода или получения информации.  CHAR * get_error_text(int code)  void err(char * pl) // выдача сообщений об ошибках.  int testnew(pack st, pack nd) // проверка целостности информации из пакета (сравнение прошлой и текущей карт при получении информации. При отличии карты на одну координату происходит отправка подтверждения и обновление карты игрока).

4. Процессы.

В процессе работы периодически проверяется наличие связи между клиентом и сервером (пакеты типа 3 и 4). При ходе игрока отправляется информация о ходе до получения подтверждения. При отсутствии подтверждения (в рамках определенного времени Ц timeout на подтверждение) отправка информации повторяется периодически до получения подтверждения. Случайная потеря или повреждение пакета не приведет к некорректности работы т.к. осуществляется проверка целостности данных. Программа завершает работу по команде пользователя или при timeout-е.

Диаграммы:

5. Текст программы.

#include "stdafx.h" #include "resource.h" #include <winsock2.h> #include <stdlib.h> #include <stdio.h> #define MAX_LOADSTRING 100 #define WSA_ACCEPT (WM_USER + 1) #define WSA_NETEVENT (WM_USER + 2) #define VALIDSOCK(s) ((s)>=0) #define DERROR GetLastError() #define CLOSEROUTINE(s) closesocket(s) #define TRETCODE WORD /* * Client interaction routine data */ typedef struct _THREAD_DATA { SOCKET client; struct sockaddr_in address; } THREAD_DATA, * PTHREAD_DATA; struct cs { SOCKET client,server; } mcs; struct pack{ char action, step; char matr[4][4]; } ar; union packd { char buff[63]; struct pack p; }; int tr = 0, trd = 0; /* * socket adrress type converter routine */ #define SACONV(a) (struct sockaddr *)a void err(char * pl); void drawme(HWND hWnd); void ServerStop(HWND hWnd); HINSTANCE hInst; // current instance TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); bool iamserv = false,started = false, iamclient = false, selected = false, sstep = false, cstep = true; SOCKET s,s_cl=0,client=0; /* Client connection socket */; SOCKADDR_IN dest_sin; struct sockaddr_in caddr; /* Local socket bind address */ struct sockaddr_in saddr; /* Remote (target) address */ struct sockaddr_in local_cl; /* Local client address */ HANDLE thread,thread_cl,thread_draw, thread_put; #define SERV_PORT 55555 #define CLNT_PORT 55556 #define PLUS 1 #define ZERO 2 #define SIDE 40 struct timeval tv; HANDLE finished, finished_cl, finished_draw,finished_put; CHAR * get_error_text(int code) { switch (code) { case (WSANOTINITIALISED): return "Winsock was not initialized"; case (WSAENETDOWN): return "Network subsystem failed"; case (WSAEADDRINUSE): return "local addres already used"; case (WSAEINTR): return "call cancelled"; case (WSAEINPROGRESS): return "operation in progress"; case (WSAEALREADY): return "already connecting"; case (WSAEADDRNOTAVAIL): return "local address not available"; case (WSAEAFNOSUPPORT): return "address family not supported"; case (WSAECONNREFUSED): return "connection refused (port closed)"; case (WSAEFAULT): return "memory access violation"; case (WSAEINVAL): return "invalid socket operation"; case (WSAEISCONN): return "already connected"; case (WSAENETUNREACH): return "network unreachable"; case (WSAENOBUFS): return "no buffer space available"; case (WSAENOTSOCK): return "not a socket"; case (WSAETIMEDOUT): return "timeout"; case (WSAEWOULDBLOCK): return "socket nonblocking, but operation blocks"; case (WSAEACCES): return "broadcast address given"; } return "Unknown error"; } int cmpstruct(pack st, pack nd){ int i,j,k=0; for(i=0; i<=3; i++) { for(j=0;j<=3;j++) { if(st.matr[i][j] != nd.matr[i][j]) { k++; } } } return(k); } int testnew(pack st, pack nd){ int i,j,k=0; for(i=0; i<=3; i++) { for(j=0;j<=3;j++) { if((st.matr[i][j] != nd.matr[i][j]) && st.matr[i][j] == 0 && nd.matr[i][j] != 0) { k = k + 1; } } } return(k); } PTHREAD_DATA dt=NULL; DWORD WINAPI ThreadAction_serv(LPVOID param) { union packd p; memset(&p,0,sizeof(p)); // struct sockaddr addr; struct sockaddr_in addr; addr.sin_addr.s_addr = INADDR_ANY; int size = sizeof(caddr),tmp = -1,e; fd_set rfds; struct timeval tv; HWND hWnd = HWND(param); FD_ZERO(&rfds); FD_SET(s, &rfds); tv.tv_sec = 8192; tv.tv_usec = 0; int retval = select(1, &rfds, NULL, NULL, &tv); while (retval) { e = recvfrom(s,p.buff, sizeof(p.buff)+1,0,(sockaddr*)&caddr,&size); caddr.sin_port = htons(CLNT_PORT); switch(p.p.action){ case('p'): Sleep(1); p.p = ar; e = sendto(s_cl, p.buff, sizeof(p)+1,0,(PSOCKADDR)&caddr, sizeof(caddr)); if (e == -1) err("serv ping send"); started = true; tmp = testnew(ar,p.p); if (tmp < 2 && tmp != 0 ){ ar = p.p; } drawme(hWnd); break; case('i'): tmp = testnew(ar,p.p); if (tmp < 2 && tmp != -1) { ar = p.p; drawme(hWnd); p.p.action = 'r'; e = sendto(s_cl, p.buff, sizeof(p)+1,0,(PSOCKADDR)&caddr, sizeof(caddr)); if (e == -1) err("serv reply send"); } break; case('r'): tmp = testnew(ar,p.p); trd++; tr = 0; break; } tv.tv_sec = 4 * 60; retval = select(1, &rfds, NULL, NULL, &tv); } MessageBox(NULL,"end of serv die to timeout...","tip",MB_OK); closesocket(s); SetEvent(finished); return(0); } DWORD WINAPI ThreadAction_cl(LPVOID param) { union packd p; memset(&p,0,sizeof(p)); struct sockaddr_in addr; int size = sizeof(addr),tmp,e; fd_set rfds; struct timeval tv; HWND hWnd = HWND(param); lstrcpy(p.buff,"pest string at client start..."); saddr.sin_addr.s_addr = inet_addr("192.168.86.171"); memset(&p,0,sizeof(p)); p.p.action = 'p'; sendto(s, p.buff, sizeof(p)+1,0,(PSOCKADDR)&saddr, sizeof(saddr)); if (e == -1) err("client 1st ping send"); FD_ZERO(&rfds); FD_SET(s_cl, &rfds); tv.tv_sec = 4 * 60; tv.tv_usec = 0; int retval; /* SetEvent(finished); closesocket(s_cl); return(0); */ retval = select(1, &rfds, NULL, NULL, &tv); while (retval) { e = recvfrom(s_cl,p.buff,sizeof(p.buff)+1,0,(sockaddr*)&addr,&size); saddr.sin_port = htons(SERV_PORT); switch(p.p.action) { case('p'): p.p = ar; e = sendto(s, p.buff, sizeof(p)+1,0,(PSOCKADDR)&saddr, sizeof(saddr)); if (e == -1) err("client ping send"); started = true; if (testnew(ar,p.p) < 2) { ar = p.p; } drawme(hWnd); break; case('i'): tmp = testnew(ar,p.p); if (tmp < 2 && tmp != 0 ) { ar = p.p; drawme(hWnd); p.p.action = 'r'; e = sendto(s, p.buff, sizeof(p)+1,0,(PSOCKADDR)&saddr, sizeof(saddr)); if (e == -1) err("client reply error"); } break; case('r'): tmp = testnew(ar,p.p); trd++; tr = 0; if (tmp != 0 ){ MessageBox(NULL,"something wrong","At server got: ",MB_OK); } break; } memset(&p,0,sizeof(p)); retval = select(1, &rfds, NULL, NULL, &tv); } MessageBox(NULL,"end of client (ie timeout...)","tip",MB_OK); closesocket(s_cl); SetEvent(finished); return(0); } DWORD WINAPI ThreadAction_put(LPVOID param) { union packd pa; /*CString ss; ss= (char*)param; pa.buff = ss; */ /* char * ss = (char *)param; for (int i = 0 ; i <= sizeof(pa.buff); i++) { pa.buff[i] = ss[i]; } */ pa.p = ar; pa.p.action = 'i'; int e; tr ++; while (tr >= trd) { if (! iamserv && iamclient) { saddr.sin_port = htons(SERV_PORT); e = sendto(s, pa.buff, sizeof(pa.buff)+1, 0,(PSOCKADDR)&saddr, sizeof(saddr)); } ; if (iamserv && ! iamclient){ caddr.sin_port = htons(CLNT_PORT); e = sendto(s_cl, pa.buff, sizeof(pa.buff)+1, 0,(PSOCKADDR)&caddr, sizeof(caddr)); } Sleep(1); } trd = 0; tr = 0; return(0); } void putplus(HWND hWnd, char flag,char i, char j) { union packd p; // p.p = ar; // p.p.action = 'i'; int e, timer; if (ar.matr[i][j]==0) ar.matr[i][j] = flag; ar.action = 'i'; p.p = ar; drawme(hWnd); CloseHandle(finished_put); DWORD threadID; finished_put = CreateEvent(NULL,TRUE,FALSE,NULL); thread_put = CreateThread( NULL,65536,ThreadAction_put,(LPVOID)p.buff,CREATE_SUSPENDED,&threadID ); ResumeThread(thread_put); return; // return(true); } void drawme(HWND hWnd){ HDC hdc = GetDC(hWnd); DWORD threadID; int x,y; for (x = 1 ; x<=3; x++) { for(y = 1; y<=3; y++) { if (ar.matr[x][y] == ZERO) Ellipse(hdc,x*SIDE,y*SIDE,(x+1)*SIDE,(y+1)*SIDE); // putplus(PLUS,x,y); if (ar.matr[x][y] == PLUS) { MoveToEx(hdc,x*SIDE,y*SIDE,NULL); LineTo(hdc,(x+1)*SIDE,(y+1)*SIDE); MoveToEx(hdc,(x+1)*SIDE,y*SIDE,NULL); LineTo(hdc,x*SIDE,(y+1)*SIDE); } } } // MoveTo(x * side, y* side); // LineTo((x+1)* side, (y+1)*side); /* if (x < 4 && x>0 && y < 4 && y > 0) { Ellipse(hdc,x*side,y*side,(x+1)*side,(y+1)*side); putplus(PLUS,x,y); MoveToEx(hdc, x*side,y*side,NULL); LineTo(hdc,(x+1)*side,(y+1)*side); MoveToEx(hdc,(x+1)*side,y*side,NULL); LineTo(hdc,x*side,(y+1)*side); } */ // putminus(x,y); ReleaseDC(hWnd, hdc); return; finished_draw = CreateEvent(NULL,TRUE,FALSE,NULL); thread_draw = CreateThread( NULL,65536,ThreadAction_cl,(LPVOID)hWnd,CREATE_SUSPENDED,&threadID ); ResumeThread(thread_draw); started=true; } DWORD WINAPI ThreadAction_drawme(LPVOID param) { HDC hdc = GetDC((HWND)param); Ellipse(hdc,2 * SIDE, 2 * SIDE, (2+1) * SIDE,(2+1) * SIDE); ReleaseDC((HWND)param, hdc); SetEvent(finished_draw); return(2); } void initme(){ s = socket(AF_INET,SOCK_DGRAM,IPPROTO_IP); saddr.sin_family=AF_INET; /* Target address is IP */ saddr.sin_port=htons(SERV_PORT); saddr.sin_addr.s_addr = INADDR_ANY; s_cl = socket(AF_INET,SOCK_DGRAM,IPPROTO_IP); caddr.sin_family = AF_INET; caddr.sin_addr.s_addr = INADDR_ANY; //= inet_addr("127.168.86.167"); caddr.sin_port = htons(CLNT_PORT); } void startserv(HWND hWnd) { // Handle для Thread'а, используется в Resume/Suspend/Destroy DWORD threadID; // ThreadID, используется для //[13:05:31] <Dalth> //OpenThread, по-моему, и для некоторых других целей if (bind(s,SACONV(&saddr),sizeof(saddr))!=0){ MessageBox(NULL,"bind at server error","bind",MB_OK); }; finished = CreateEvent(NULL,TRUE,FALSE,NULL); thread = CreateThread( NULL,65536,ThreadAction_serv,(LPVOID)hWnd,CREATE_SUSPENDED,&threadID ); ResumeThread(thread); } bool clientconnect(HWND hWnd) { CloseHandle(finished_cl); DWORD threadID; int rc = bind(s_cl,SACONV(&caddr),sizeof(caddr)); if ( rc != 0 ) { } // here was bug finished_cl = CreateEvent(NULL,TRUE,FALSE,NULL); thread_cl = CreateThread( NULL,65536,ThreadAction_cl,(LPVOID)hWnd,CREATE_SUSPENDED,&threadID ); ResumeThread(thread_cl); return(true); } void ServerStop(HWND hWnd) { CloseHandle(finished); CloseHandle(finished_draw); CloseHandle(finished_cl); CloseHandle(finished_put); started = false; closesocket(s); closesocket(s_cl); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){ // TODO: Place code here. MSG msg; HACCEL hAccelTable; // Initialize global strings LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_SERVE, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_SERVE); // Main message loop: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_SERVE); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = (LPCSTR)IDC_SERVE; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); return RegisterClassEx(&wcex); } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // Store instance handle in our global variable // hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, // CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, 200, 256, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; RECT rt; int wmId, wmEvent,rc; TCHAR szHello[MAX_LOADSTRING]; LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); unsigned char i,j, x,y; switch (message) { case WM_CREATE: WSADATA WSAData; // Инициализация и проверка версии Windows Sockets rc = WSAStartup(MAKEWORD(1, 1), &WSAData); initme(); if (rc != 0){ MessageBox(NULL, "WSAStartup Error", "Error", MB_OK); return FALSE; } else { return TRUE; } case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; case IDM_EXIT: DestroyWindow(hWnd); break; case IDM_START: if (! iamclient) { selected = true; iamserv = true; startserv(hWnd); memset(&ar,0,sizeof(ar)); ar.step = 1; } else { MessageBox(NULL,"I am already client...","tip",MB_OK); } break; case IDM_STOP: selected = false; // if (iamserv) ServerStop(hWnd); iamserv = false; iamclient = false; break; case IDM_CONNECT: if ( ! iamserv && clientconnect(hWnd)) { memset(&ar,0,sizeof(ar)); iamclient = true; selected = true; } else { MessageBox(NULL,"I am already server...","tip",MB_OK); } break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_LBUTTONDOWN: x = (char)(LOWORD(lParam) / SIDE); y = (char)(HIWORD(lParam) / SIDE ); sstep = ( 2 * (char)(ar.step / 2 ) != (char)ar.step); if (iamserv && ! iamclient ) { if (ar.matr[x][y] == 0 ) { ar.matr[x][y] = PLUS; ar.step = ar.step + 1; putplus(hWnd,PLUS,x,y); } } cstep = ( 2 * (char)(ar.step / 2 ) == (char)ar.step); if (! iamserv && iamclient) { if (ar.matr[x][y] == 0 ) { ar.matr[x][y] = ZERO; ar.step = ar.step + 1; putplus(hWnd,ZERO,x,y); } } // if ( 2 * (char)(ar.step / 2 ) == (char)ar.step) ar.matr[x][y] = ZERO; // if ( 2 * (char)(ar.step / 2 ) != (char)ar.step) ar.matr[x][y] = PLUS; // drawme(hWnd); break; case WM_RBUTTONDOWN: x = (char)(LOWORD(lParam) / SIDE ); y = (char)(HIWORD(lParam) / SIDE ); ar.matr[x][y] = PLUS; drawme(hWnd); break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: Add any drawing code here... GetClientRect(hWnd, &rt); for (i=1;i<=3;i++) { for (j=1;j<=3;j++) { Rectangle(hdc,SIDE *i,SIDE*j,SIDE * i + SIDE,SIDE *j + SIDE); } } EndPaint(hWnd, &ps); break; case WM_DESTROY: ServerStop(hWnd); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return DefWindowProc(hWnd, message, wParam, lParam); } LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; } { void err(char * pl) { MessageBox(NULL,get_error_text(WSAGetLastError()), pl,MB_OK); }