ФЕДЕРАЛЬНОЕ АГЕНТСТВО СВЯЗИ ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ «СИБИРСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ И ИНФОРМАТИКИ» Кафедра Прикладной Математики и Кибернетики КУРСОВОЙ ПРОЕКТ по дисциплине «Операционные Системы» «Консольная Многопользовательская Онлайн Игра на Сокетах» Выполнил: Пекарский Р. И. 2 курс, группа П-91 Проверил преподаватель кафедры ПМиК Малков Е. А. Оценка:_______________ Дата:_______________ Новосибирск – 2011 1 Задание на курсовую работу 1. 2. Разработать сервер на языке СИ. Разработать программу - клиент на языке СИ с обменом информации через сервер. Часть 1. Разработать сервер на языке СИ Описание основных функций сервера: CreateUser: основная функция обработки сообщений с программы-клиента, вызываемая системным вызовом _beginthread(), занимающаяся получением от клиента данных по сокету client_socket и вызовом других функций по обработке выстрелов Shoot() и перемещений CharacterMove (). CharacterMove: Функция изменения координаты передвигающегося персонажа. Shoot: Функция, вызываемая системным вызовом _beginthread() из функции CreateUser(), ответственная за создание снаряда, с условием что у атакующего персонажа число патронов больше нуля. При перемещении снаряда проверяется его столкновение с другими персонажами, при попадании на какого-либо персонажа, отнимает его здоровье, при здоровье <=0 – убивает персонаж, с дальнейшей его регенерацией в другой точке игрового поля и сообщением о смерти и сообщением о успешном убийстве атакующему персонажу. Dead: Функция, вызываемая функцией Shoot(), обрабатывающая событие смерти. 2 Исходный код srv2.c #include #include #include #include #include <stdio.h> <winsock2.h> <windows.h> <process.h> <string.h> struct User { struct sockaddr_in client_addr; SOCKET client_socket; } user[16]; struct Character{ char name[20]; int x; int y; int hp; int ID; int turn; int ammo; int st; } character[16]; struct sh{ int X; int Y; int pl; }shoot; int character_size = sizeof(struct Character); int pocket_size; int character_size_ID = sizeof(character[0].ID); char serverName[120]; char buff[1024]; int buff_size = sizeof(buff); int MY_PORT = 1952; int i=0; void CreateUser(int*); void CharacterMove(char, struct Character* ); void Shoot(struct Character* ); void Dead(); int main(int argc, char* argv[]) { int c; SOCKET mysocket, client_socket; struct sockaddr_in local_addr, client_addr; int client_addr_size=sizeof(client_addr); /* Получение настроек */ SetConsoleTitle("Server"); printf("Enter server port: "); scanf("\n%i",&MY_PORT); printf("Enter server name: "); scanf("\n%s",serverName); sprintf(buff,"%s on %i port",serverName,MY_PORT); SetConsoleTitle(buff); if (WSAStartup(0x0202,(WSADATA *) &buff[0])) { printf("Error WSAStartup %d\n", WSAGetLastError()); return -1; } if ((mysocket=socket(AF_INET,SOCK_STREAM,0))<0){ printf("Error socket %d\n",WSAGetLastError()); WSACleanup(); return -1; } local_addr.sin_family=AF_INET; local_addr.sin_port=htons(MY_PORT); local_addr.sin_addr.s_addr=0; if (bind(mysocket,(struct sockaddr *) &local_addr,sizeof(local_addr))) { printf("Error bind %d\n",WSAGetLastError()); closesocket(mysocket); WSACleanup(); return -1; } if (listen(mysocket, 0x100)){ 3 printf("Error listen %d\n",WSAGetLastError()); closesocket(mysocket); WSACleanup(); return -1; } printf("+Server started on %i port\n+Waiting for users.\n",MY_PORT); /* Цикл отслеживания подключения поных клиентов */ while((client_socket=accept(mysocket, (struct sockaddr *) &client_addr, &client_addr_size))){ i++; recv(client_socket, (char*)&pocket_size, sizeof(int),0); if(pocket_size == sizeof(character[0])){ send(client_socket,(char*)&i,sizeof(int),0); recv(client_socket, (char*)&character[i].name, sizeof(character[0].name),0); } user[i].client_addr = client_addr; user[i].client_socket = client_socket; /* Запуск потока клиента */ _beginthread(CreateUser,0,&i); } return 0; } /* Поток обработки клиентских сообщений void CreateUser(int* i){ int j; int c; char temp[1024]; char buff[1024]; char name[20]; SOCKET client_socket; struct sockaddr_in client_addr; struct hostent *hst; int bytes_recv; int temp_size = sizeof(temp); j=*i; client_addr = user[j].client_addr; client_socket = user[j].client_socket; sprintf(name,character[j].name); character[j].x = (rand()*j)%30; character[j].y = (rand()*j)%15; character[j].hp = 100; character[j].ammo = 3; character[j].ID = j; character[j].st = 1; */ send(client_socket,(char*)&character_size,sizeof(int),0); send(client_socket,(char*)&character[j],sizeof(character[j]),0); hst=gethostbyaddr((char *)&client_addr.sin_addr.s_addr,4,AF_INET); printf("+%s [%s] connected..\n", name, inet_ntoa(client_addr.sin_addr)); sprintf(temp,"+Hello to %s %s !\n",serverName,name); send(client_socket,(char*)&temp_size,sizeof(int),0); send(client_socket,temp,sizeof(temp),0); printf("sended = %s to %s\n",temp,name); for (c=1;c<16;c++) { if (character[c].ID != 0){ send(user[j].client_socket,(char*)&character_size_ID,sizeof(int),0); send(user[j].client_socket,(char*)&character[c].ID,sizeof(int),0); printf("send = %i\n",character_size); send(user[j].client_socket,(char*)&character[c],sizeof(character[c]),0); } } for (c=1;c<16;c++) { if (character[j].ID != 0){ send(user[c].client_socket,(char*)&character_size_ID,sizeof(int),0); send(user[c].client_socket,(char*)&character[j].ID,sizeof(int),0); printf("%s send = %i\n",character[j].name,character_size); send(user[c].client_socket,(char*)&character[j],sizeof(character[j]),0); } } /* Цикл прослушивания данных сокета */ while( ( bytes_recv=recv(client_socket, (char*)&pocket_size, sizeof(int),0) ) 4 && bytes_recv !=SOCKET_ERROR ){ /* Если пришло сообщение */ if(pocket_size == sizeof(buff)){ recv(client_socket, (char*)&buff, sizeof(buff),0); /* Если это сообщение не команда - то сообщение чата */ if(buff[0] != '+'){ printf("[%s]: %s",name,buff); sprintf(temp,"[%s]: %s",name,buff); for (c=1;c<16;c++) { send(user[c].client_socket, (char*)&temp_size, sizeof(int), 0); send(user[c].client_socket, (char*)temp, temp_size, 0); } } else { /* Если это сообщение команда */ switch (buff[1]){ case 'a':printf("%s Attack\n",name); if(character[j].ammo != 0) _beginthread (Shoot,0,&character[j]); break; case 'l':printf("%s Move Left\n",name); CharacterMove(buff[1],&character[j]) ;break; case 'r':printf("%s Move Right\n",name); CharacterMove(buff[1],&character[j]); break; case 'u':printf("%s Move Up\n",name); CharacterMove(buff[1],&character[j]); break; case 'd':printf("%s Move Down\n",name); CharacterMove(buff[1],&character[j]); break; case 'h':printf("%s Heals\n",name);break; } for (c=1;c<16;c++) { if (character[j].st == 1){ send(user[c].client_socket,(char*)&character_size_ID,sizeof(int),0); send(user[c].client_socket,(char*)&character[j].ID,sizeof(int),0); send(user[c].client_socket,(char*)&character[j],sizeof(character[j]),0); } } } } } /* Отключение клиента или ошибка */ for (c=1;c<16;c++) { if (character[j].st == 1){ send(user[c].client_socket,(char*)&character_size_ID,sizeof(int),0); send(user[c].client_socket,(char*)&character[j].ID,sizeof(int),0); send(user[c].client_socket,(char*)&character[j],sizeof(character[j]),0); } } for (c=1;c<16;c++) { if (character[j].st == 1){ send(user[c].client_socket, (char*)&temp_size, sizeof(int), 0); sprintf(temp,"+%s was disconnected\n",character[j].name); send(user[c].client_socket, (char*)temp, temp_size, 0); } } character[j].st = 0; printf("+ %s was disconnected\n",name); closesocket(client_socket); } /* Перемещение персонажа */ void CharacterMove(char ch, struct Character* character){ switch (ch){ 5 case case case case } 'l':if(character->x 'r':if(character->x 'u':if(character->y 'd':if(character->y > < > < 1) character->x -=1;character->turn = 4;break; 30) character->x +=1;character->turn = 2;break; 1) character->y -=1;character->turn = 1;break; 15) character->y +=1;character->turn = 3;break; } /* Обработка снарядов */ void Shoot(struct Character* CH){ int l,c,j,d,turn; struct sh{ int X; int Y; int pl; }shoot; int shoot_size = sizeof(shoot); int ded = CH->ID; /* Проверка на наличие патронов */ if (CH->ammo != 0){ CH->ammo --; shoot.X=CH->x; shoot.Y=CH->y; shoot.pl = CH-> ID; turn = CH->turn; for(l=0;l<7;l++){ switch (turn){ case 1:shoot.Y--;break; case 2:shoot.X++;break; case 3:shoot.Y++;break; case 4:shoot.X--;break; } if (shoot.Y > 14 || shoot.Y < 1 || shoot.X < 1 || shoot.X > 29) break; for (c=1;c<16;c++) { /* Проверка столкновений с персонажами */ if (shoot.Y > 14 || shoot.Y < 1 || shoot.X < 1 || shoot.X > 29) break; if(shoot.X == character[c].x && shoot.Y == character[c].y && character[c].ID != shoot.pl){ /* Попадание */ character[c].hp -= 10; /* Смерть */ if (character[c].hp <=0) Dead(&character[c],&shoot); } if (character[c].ID != 0){ send(user[c].client_socket, (char*)&shoot_size, sizeof(int), 0); printf("+attack! %i %i %i\n",l,shoot.X,shoot.Y); send(user[c].client_socket, (char*)&shoot, sizeof(shoot), 0); } } Sleep(150); } CH->ammo++; } } /* Смерть персонажа и дальнейшее перерождение */ void Dead(struct Character* CH, struct sh* shoot){ int i,j,c; i = CH->ID; j = shoot->pl; CH->x = rand()%30; CH->y = rand()%15; CH->hp = 100; send(user[i].client_socket, (char*)&buff_size, sizeof(int), 0); sprintf(buff,"+You are dead!\n"); send(user[i].client_socket, (char*)&buff, sizeof(buff), 0); send(user[j].client_socket, (char*)&buff_size, sizeof(int), 0); sprintf(buff,"+You frag %s!\n",CH->name); send(user[j].client_socket, (char*)&buff, sizeof(buff), 0); for (c=1;c<16;c++) { send(user[c].client_socket,(char*)&character_size_ID,sizeof(int),0); send(user[c].client_socket,(char*)&character[i].ID,sizeof(int),0); send(user[c].client_socket,(char*)&character[i],sizeof(character[i]),0); } } 6 Часть 2. Разработать программу - клиент на языке СИ с обменом информации через сервер Описание основных функций клиетна: CreateRecieveThread: основная функция обработки сообщений с сервера, вызываемая системным вызовом _beginthread(); MoveChar: Очистка игрового поля с последующей отрисовкой персонажей. NewMassage: Функция отвечающая за вывод сообщений в чате. Shoot: Отрисовка летящих снарядов. PrintInfo: Функция отвечающая за вывод информации о персонаже. PrintPl: Функция отвечающая за вывод подключенных пользователей. 7 Исходный код cln3.c #include #include #include #include #include <stdio.h> <string.h> <winsock2.h> <windows.h> <process.h> char buff[1024]; SOCKET my_sock; struct _user{ struct sockaddr_in dest_addr; struct hostent *hst; char name[20]; } user; struct sh{ int X; int Y; int pl; }shoot; struct Character{ char name[20]; int x; int y; int hp; int ID; int turn; int ammo; int st; } character[16]; int character_size = sizeof(struct Character); int pocket_size,bytes_recv,buff_size=sizeof(buff),shoot_size = sizeof(shoot); int ID,f,MyNumber; char ch; char nickname[128]; char temp[100]; char buffs[8][256]; int PORT; char SERVERADDR[16]; void CreateRecieveThread(char*); HANDLE Hnd; COORD Crd,Crd2,Crd3,Crdn,Crdsht; COORD Screen; CONSOLE_SCREEN_BUFFER_INFO ConsScreenBuf; void void void void void MoveChar(); NewMassage(char*); Shoot(struct sh*); printInfo(); printPl(); int main(int argc, char* argv[]){ int i,j; for (j=0;j<16;j++){ character[j].x = 0; character[j].y = 0; } Screen.X = 50; Screen.Y = 30; system("cls"); Hnd=GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTitle("Game Client"); SetConsoleScreenBufferSize(Hnd,Screen); /* Получение настроек */ printf("Enter server IP: "); scanf("\n%s",&SERVERADDR); printf("Enter server port: "); scanf("\n%i",&PORT); printf("Enter your nickname: "); scanf("%s",nickname); sprintf(temp,"Game Client | %s | %s:%i",nickname,SERVERADDR,PORT); SetConsoleTitle(temp); if (WSAStartup(0x202,(WSADATA *)&buff[0])){ 8 printf("WSAStart error %d\n",WSAGetLastError()); return -1; } my_sock=socket(AF_INET,SOCK_STREAM,0); if (my_sock < 0){ printf("Socket() error %d\n",WSAGetLastError()); return -1; } user.dest_addr.sin_family=AF_INET; user.dest_addr.sin_port=htons(PORT); if (inet_addr(SERVERADDR)!=INADDR_NONE) user.dest_addr.sin_addr.s_addr=inet_addr(SERVERADDR); else if (user.hst=gethostbyname(SERVERADDR)) ((unsigned long *)&user.dest_addr.sin_addr)[0]= ((unsigned long **)user.hst->h_addr_list)[0][0]; else { printf("Invalid address %s\n",SERVERADDR); closesocket(my_sock); WSACleanup(); return -1; } if (connect(my_sock,(struct sockaddr *)&user.dest_addr, sizeof(user.dest_addr))){ printf("Connect error %d\n",WSAGetLastError()); return -1; } printf("Connection with %s was established\n\ Type quit for quit\n\n",SERVERADDR); /* Удачное подключение */ send(my_sock,(char*)&character_size,sizeof(int),0); printf("sended = %i\n",character_size); recv(my_sock, (char*)&ID, sizeof(int),0); printf("ID is = %i\n",ID); MyNumber = ID; printf("MyNumber is = %i\n",MyNumber); send(my_sock,(char*)&nickname,sizeof(character[ID].name),0); system("cls"); GetConsoleScreenBufferInfo(Hnd,&ConsScreenBuf); /* Вывод графических элементов */ Crd.X = 0; Crd.Y = 0; SetConsoleCursorPosition(Hnd,Crd); putchar(218); for(i=0;i<30;i++) putchar(196); putchar(191); for(i=1;i<16;i++){ Crd.Y = i; SetConsoleCursorPosition(Hnd,Crd); putchar(179); printf(" putchar(179); "); } Crd.Y = 16; SetConsoleCursorPosition(Hnd,Crd); putchar(192); for(i=0;i<30;i++) putchar(196); putchar(217); Crd.X = 48; Crd.Y = 0; SetConsoleCursorPosition(Hnd,Crd); putchar(218); for(i=0;i<20;i++) putchar(196); putchar(191); for(i=1;i<18;i++){ Crd.Y = i; 9 SetConsoleCursorPosition(Hnd,Crd); putchar(179); printf(" "); putchar(179); } Crd.Y = 18; SetConsoleCursorPosition(Hnd,Crd); putchar(192); for(i=0;i<20;i++) putchar(196); putchar(217); /* Запуск потока обработки */ _beginthread(CreateRecieveThread,0,&nickname); /* Цикл обработки сообщений чата (комманд) */ while(1){ ch = getch(); if(ch == 13){ Crd.X = 1; Crd.Y = 30; SetConsoleCursorPosition(Hnd,Crd); printf(" SetConsoleCursorPosition(Hnd,Crd); fgets(buff, sizeof(buff), stdin); if (!strcmp(buff,"quit\n")){ printf(" Exit..."); closesocket(my_sock); WSACleanup(); return 0; } /* Если сообщение не пустое и не команда */ if (strcmp(buff,"\n")) if (buff[0]!=' ') { send(my_sock,(char*)&buff_size,sizeof(int),0); send(my_sock,(char*)buff,sizeof(buff),0); } } if(ch == 75){ sprintf(buff,"+l"); send(my_sock,(char*)&buff_size,sizeof(int),0); send(my_sock,(char*)buff,sizeof(buff),0); } if(ch == 72){ sprintf(buff,"+u"); send(my_sock,(char*)&buff_size,sizeof(int),0); send(my_sock,(char*)buff,sizeof(buff),0); } if(ch == 80){ sprintf(buff,"+d"); send(my_sock,(char*)&buff_size,sizeof(int),0); send(my_sock,(char*)buff,sizeof(buff),0); } if(ch == 77){ sprintf(buff,"+r"); send(my_sock,(char*)&buff_size,sizeof(int),0); send(my_sock,(char*)buff,sizeof(buff),0); } if(ch == 32){ sprintf(buff,"+a"); send(my_sock,(char*)&buff_size,sizeof(int),0); send(my_sock,(char*)buff,sizeof(buff),0); } } printf("Recv error %d\n",WSAGetLastError()); closesocket(my_sock); WSACleanup(); return -1; } /* Поток получения сообщений с сервера void CreateRecieveThread(char* name){ int pocket_size; "); */ 10 char buff[1024]; struct sh shot; while(bytes_recv=recv(my_sock,(char*)&pocket_size,sizeof(int),0) && bytes_recv != SOCKET_ERROR){ if (pocket_size == sizeof(character[ID].ID)){ /* если сообщение о структурах персонажей */ recv(my_sock,(char*)&ID,sizeof(int),0); recv(my_sock,(char*)&character[ID],sizeof(character[ID]),0); if (character[ID].ID != 0) { MoveChar(); printInfo(); } } else if (pocket_size == buff_size ){ /* если сообщение текстовое */ recv(my_sock, (char*)&buff, sizeof(buff),0); NewMassage(buff); printInfo(); } else if (pocket_size == shoot_size ){ /* если сообщение о снарядах */ recv(my_sock, (char*)&shot, sizeof(shot),0); Shoot(&shot); printInfo(); } } } /* отрисовка игрового поля */ void MoveChar(){ int i; GetConsoleScreenBufferInfo(Hnd,&ConsScreenBuf); Crd.X = 1; for(i=1;i<16;i++){ Crd.Y = i; SetConsoleCursorPosition(Hnd,Crd); printf(" "); } for (i=0;i<16;i++){ if (character[i].st == 1){ Crdn.X = character[i].x; Crdn.Y = character[i].y; SetConsoleCursorPosition(Hnd,Crdn); putchar(1); } } Crdn.X = character[MyNumber].x; Crdn.Y = character[MyNumber].y; SetConsoleCursorPosition(Hnd,Crdn); putchar(2); switch (character[MyNumber].turn){ case 1: if(character[MyNumber].y >1){ Crdn.X = character[MyNumber].x ; Crdn.Y = character[MyNumber].y-1; SetConsoleCursorPosition(Hnd,Crdn); putchar(30);} ;break; case 2: if(character[MyNumber].x <30){ Crdn.X = character[MyNumber].x +1; Crdn.Y = character[MyNumber].y; SetConsoleCursorPosition(Hnd,Crdn); putchar(16);} ;break; case 3: if(character[MyNumber].y <15){ Crdn.X = character[MyNumber].x ; Crdn.Y = character[MyNumber].y+1; SetConsoleCursorPosition(Hnd,Crdn); putchar(31);} ;break; case 4: if(character[MyNumber].x >1){ Crdn.X = character[MyNumber].x -1; Crdn.Y = character[MyNumber].y; 11 SetConsoleCursorPosition(Hnd,Crdn); putchar(17);} ;break; } printPl(); SetConsoleCursorPosition(Hnd,ConsScreenBuf.dwCursorPosition); } /* Вывод на экран сообщений чата */ void NewMassage(char* buff){ int bi=0,bb=0,i; Crd2.X = 1; Crd2.Y = 18; GetConsoleScreenBufferInfo(Hnd,&ConsScreenBuf); SetConsoleCursorPosition(Hnd,Crd2); for (bi=7;bi>-1;bi--){ strcpy(buffs[bi],buffs[bi-1]); } strcpy(buffs[0],buff); for (bi=8;bi>-1;bi--){ Crd2.Y = 18+bi; if (strlen(buffs[bi])<43){ printf(" \n"); } else { for (bb=0;bb<43;bb++) printf(" \n"); Crd2.Y++; SetConsoleCursorPosition(Hnd,Crd2); for (bb=0;bb<43;bb++) printf(" \n"); } } for (bi=0;bi<8;bi++){ Crd2.Y++; SetConsoleCursorPosition(Hnd,Crd2); if (strlen(buffs[bi])<43){ if(buffs[bi][0]=='+') SetConsoleTextAttribute(Hnd,FOREGROUND_GREEN); printf("%s",buffs[bi]); SetConsoleTextAttribute(Hnd,FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED); } else { for (bb=0;bb<43;bb++){ printf("%c",buffs[bi][bb]); } Crd2.Y++; SetConsoleCursorPosition(Hnd,Crd2); for (bb=0;bb<43;bb++){ printf("%c",buffs[bi][bb+43]); } } printPl(); } SetConsoleCursorPosition(Hnd,ConsScreenBuf.dwCursorPosition); } /* отрисовка снарядов */ void Shoot(struct sh* shoot) { COORD Crd; Crd.X=shoot->X; Crd.Y=shoot->Y; SetConsoleCursorPosition(Hnd,Crdsht); putchar(32); GetConsoleScreenBufferInfo(Hnd,&ConsScreenBuf); if(Crd.Y > 1 && Crd.Y < 15 && Crd.X<30 && Crd.X >1){ SetConsoleCursorPosition(Hnd,Crd); if (shoot->pl != MyNumber) SetConsoleTextAttribute(Hnd,FOREGROUND_RED); else SetConsoleTextAttribute(Hnd,FOREGROUND_BLUE|FOREGROUND_GREEN); putchar(15); Crdsht = Crd; } SetConsoleTextAttribute(Hnd,FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED); SetConsoleCursorPosition(Hnd,ConsScreenBuf.dwCursorPosition); } /* вывод информации о здоровье и тд. на экран void printInfo(){ COORD Crd; Crd.X=33; */ 12 Crd.Y=1; GetConsoleScreenBufferInfo(Hnd,&ConsScreenBuf); SetConsoleCursorPosition(Hnd,Crd); SetConsoleTextAttribute(Hnd,FOREGROUND_GREEN); printf("%s",character[MyNumber].name); Crd.Y=2; SetConsoleCursorPosition(Hnd,Crd); SetConsoleTextAttribute(Hnd,FOREGROUND_RED); printf("HP = %3.i",character[MyNumber].hp); Crd.Y=3; SetConsoleCursorPosition(Hnd,Crd); SetConsoleTextAttribute(Hnd,FOREGROUND_RED); printf("AMMO = %3.i",character[MyNumber].ammo); SetConsoleTextAttribute(Hnd,FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED); SetConsoleCursorPosition(Hnd,ConsScreenBuf.dwCursorPosition); } /* Список игроков */ void printPl(){ int i; COORD Crd; Crd.X=49; Crd.Y=0; for(i=0;i<16;i++){ Crd.Y=i+1; SetConsoleCursorPosition(Hnd,Crd); printf(" "); } Crd.Y=1; for(i=0;i<16;i++){ if(character[i].st == 1){ SetConsoleCursorPosition(Hnd,Crd); printf("%s",character[i].name); Crd.Y+=1; } } } 13