Курсовая: Сетевые крестики нолики
ЧЕЛЯБИНСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
ОТЧЕТ
о практической работе по дисциплине лСети ЭВМ
Факультет: математический Выполнила: Насибулина
Раиса Сулеймановна
Специальность: прикладная Группа: МП-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);
}