server.cpp
上传用户:loctite114
上传日期:2007-01-03
资源大小:49k
文件大小:13k
- // Copyright (c) 1999 Lee Patterson
- // leepatterson@home.com
- //
- // Demonstrats:
- //
- // o Internet Server that accepts telnet connections.
- // o Using multiple threads to handle more then one connection.
- // o BlockingSocket class for using sockets.
- // (I'd recomend this class over the MS CSocket and such, as they still have
- // some 16-bit stuff in them, and this class ports to Unix a lot easier.
- // That was the main goal of my program.)
- //
- // Also included:
- //
- // o Simple linked list class that can be used anywhere (unix/mfc/console).
- // (Note: The linked list class is not thread safe.)
- //
- // I'd be happy to field any questions about the workings
- // of the server, or how I set things up, but I'm not interested in fixing
- // up this server, or fixing any of the "chess game" bugs. Any bugs
- // found in the socket classes I would be interested in, although they are
- // throughly tested.
- //
- // I built this server one night as a demo for a company in hopes
- // of getting a job there. Didn't know a damn thing about sockets
- // so I used, and built upon a blockingsocket class that was found
- // in Inside Visual C++ 5.0 by MS PRESS.
- //
- // There are no rules to either game, so players can move whatever
- // piece to whatever square. There isn't even any error checking,
- // and you can't backspace. This is a very limited server in that
- // respect.
- /* The layout of the server is:
- 1. Create an Accept thread to accept connections on port 23 (telnet)
- 2. Get a connection, create a new accept thread to be able to
- handle more connections.
- 3. Log this person in, getting user name.
- 4. Create a lobby of sorts where you can do simple chat, create a new
- game, join a game, or watch a game.
- 5. This lobby reads a line of input from the socket, and then checks
- what the command the user typed was via ParsCmd.
- 6. When the person creates a game, we add the game object to a pending
- list, and wait for someone to join the game.
- 7. When a person JOIN's a game, we check for the game name on the pending
- list, and assign that person as the second player on that game object.
- 8. When you WATCH a game, you are added to a list of watches on that
- game object.
- The game objects:
- The base class is the BoardGame class. Chess and Checkers games are
- derived from BoardGame. This is to make a consistant interface
- for adding players to a board game, and drawing the board.
- */
- //
- // NOTE:
- //
- // There is probably a bit of an issue with people disconnecting,
- // and things not being cleaned up. I never bothered to finish that
- // off, as it wasn't important.
- //
- #include <assert.h>
- #include <memory.h>
- #include <process.h> /* _beginthread, _endthread */
- #include <iostream.h>
- #include <winsock.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h> //strstr
- #include <llist.h>
- #include "blockingsocket.h"
- #include "gameob.h"
- int repeat=1;
- #define ASSERT assert
- #define VERIFY assert
- #define BUFFERSIZE 1000
- CLList m_games; //list of Chess Game Objects (NOTE: This isn't thread safe!)
- CLList m_pending; //list of games that are waiting for second player (NOTE: This isn't thread safe!)
- CLList m_connections; //list of all the people connected
- void log(const char* s)
- {
- printf("%sn",s);
- OutputDebugString(s);
- OutputDebugString("n");
- }
- //This is the main game loop. It reads in users moves, and prints out the board
- //contents to all people in a single game.
- void GameLoop(void* pParam)
- {
- log("GameLoop start");
- BoardGame* pgame=(BoardGame*)pParam;
- char* help= "Enter the column & row of the piece you are moving from & torn"
- "in the format FFTT. (ie: moving whites queen pawn ahead one from rn"
- "column 3, row 6 to column 3, row 5 would be entered 3635.)rn";
- char* entermove="What is your move? (?=help) ";
- char* whitesmove="It's whites move.";
- char* blacksmove="It's blacks move.";
- bool bAbort=false;
- bool bMadeMove=false;
- char inbuf[10];
- try
- {
- do
- {
- //handle whites move
- pgame->Draw();
- pgame->m_black->Print(whitesmove);
- bMadeMove=false;
- do
- {
- pgame->m_white->Write(entermove,strlen(entermove));
- pgame->m_white->ReadLine(inbuf,sizeof inbuf,300);
- if(!stricmp(inbuf,"quit"))
- {
- bAbort=true;
- pgame->m_white->Print("Aborting game");
- pgame->m_black->Print("White aborted game");
- }
- else if(!strcmp(inbuf,"?"))
- {
- pgame->m_white->Print(help);
- }
- else
- {
- if(strlen(inbuf)!=4)
- pgame->m_white->Print("Syntax error");
- else
- {
- if(pgame->ValidMove(inbuf[0],inbuf[1],inbuf[2],inbuf[3]))
- {
- bMadeMove=true;
- pgame->Move(inbuf[0],inbuf[1],inbuf[2],inbuf[3]);
- }
- else
- pgame->m_white->Print("not a valid move");
- }
- }
- } while(!bMadeMove && !bAbort);
- //handle blacks move
- if(!bAbort)
- {
- pgame->Draw();
- pgame->m_white->Print(blacksmove);
- do
- {
- pgame->m_black->Write(entermove,strlen(entermove));
- pgame->m_black->ReadLine(inbuf,sizeof inbuf,300);
- if(!stricmp(inbuf,"quit"))
- {
- bAbort=true;
- pgame->m_black->Print("Aborting game");
- pgame->m_white->Print("Black aborted game");
- }
- else if(!strcmp(inbuf,"?"))
- {
- pgame->m_black->Print(help);
- }
- else
- {
- if(strlen(inbuf)!=4)
- pgame->m_white->Print("Syntax error");
- else
- {
- bMadeMove=true;
- pgame->Move(inbuf[0],inbuf[1],inbuf[2],inbuf[3]);
- }
- }
- } while(!bMadeMove && !bAbort);
- }
- } while(!bAbort);
- }
- catch(const char* e)
- {
- log(e);
- }
- CLList* plist=m_games.FindItem(pgame);
- m_games.RemoveItem(plist);
- // delete pgame;
- log("GameLoop end");
- }
- //read in the persons user name, and make sure they are a valid user
- bool Login(CTelnetSocket* psocket)
- {
- char* login="Enter your login name (only "guest" works): ";
- char line[1000];
-
- psocket->Write(login,strlen(login));
- psocket->ReadLine(line,sizeof line);
- if(stricmp(line,"guest"))
- return false;
- psocket->m_wFlags |= CTelnetSocket::FLAG_VALIDATED;
- return true;
-
- }
- ChessGameOb* FindGame(const char* pgamename)
- {
- log("Findgame");
- ChessGameOb* o;
- CLList* plist=m_games.GetHead();
- while(plist)
- {
- o=(ChessGameOb*)plist->GetItem();
- log(o->m_gamename);
- if(!stricmp(o->m_gamename,pgamename))
- {
- //found the game name
- return o;
- }
- plist=plist->GetNext();
- }
- return NULL;
- }
- ChessGameOb* FindPendingGame(const char* pgamename)
- {
- log("Find pending game");
- ChessGameOb* o;
- CLList* plist=m_pending.GetHead();
- while(plist)
- {
- o=(ChessGameOb*)plist->GetItem();
- log(o->m_gamename);
- if(!stricmp(o->m_gamename,pgamename))
- {
- //found the game name
- return o;
- }
- plist=plist->GetNext();
- }
- return NULL;
- }
- enum
- {
- CREATE=1,
- JOIN,
- WATCH,
- QUIT,
- PLAY,
- COLOR
- };
- char* cmds[]={"create","join","watch","quit","play","color"};
- int FindToken(const char* buf)
- {
- for(int i=0; i<sizeof cmds/sizeof cmds[0]; i++)
- {
- if(strstr(buf,cmds[i]))
- return i+1;
- }
- return 0;
- }
- //send the chat in buf to all connections
- void Chat(CPersonInstance* pInstance, char* buf)
- {
- CPersonInstance* o;
- CLList* plist=m_connections.GetHead();
- while(plist)
- {
- o=(CPersonInstance*)plist->GetItem();
- if(o!=pInstance)
- o->Print(buf);
- plist=plist->GetNext();
- }
- }
- //Look at what the user typed, and act upon it.
- bool ParsCmd(char* buf,CPersonInstance* pInstance)
- {
- char* SyntaxErrorMsg="Syntax error. 'create chess|checkers name' " ;
- int itoken=FindToken(buf);
- char cmd[30],parm1[30],parm2[30];
- BoardGame* pgame;
- CLList* plist;
- switch(itoken)
- {
- case WATCH:
- //user typed "watch game"
- sscanf(buf,"%s %s",cmd,parm1);
- pgame=FindGame(parm1);
- if(!pgame)
- pInstance->Print("No game found");
- else
- {
- if(!pgame->HasAllPlayers())
- pInstance->Print("Both players have to be playing befor watchin is aloud");
- else
- {
- pgame->AddWatcher(pInstance);
- pInstance->Print("You will see the board when the next person moves");
- }
- return true;
- }
- break;
- case CREATE:
- //user typed "create chess game"
- if(3!=sscanf(buf,"%s %s %s",cmd,parm1,parm2)) //get game name
- {
- pInstance->Print(SyntaxErrorMsg);
- }
- else
- {
- if(!stricmp(parm1,"checkers"))
- {
- pgame=new CheckerGameOb(pInstance,parm2);
- pInstance->Print("Created new checkers game");
- }
- else if(!stricmp(parm1,"chess"))
- {
- pInstance->Print("Created new chess game");
- pgame=new ChessGameOb(pInstance,parm2);
- }
- else
- {
- pInstance->Print(SyntaxErrorMsg);
- return false;
- }
- m_pending.AddTail(pgame);
- #if 1
- log("Pending games:");
- plist=m_pending.GetHead();
- while(plist)
- {
- pgame=(BoardGame*)plist->GetItem();
- log(pgame->m_gamename);
- plist=plist->GetNext();
- }
- #endif
- pInstance->Print("You must now wait till someone joins your game");
- return true;
- }
- break;
- case JOIN:
- //user typed "join game"
- sscanf(buf,"%s %s",cmd,parm1);
- pgame=FindPendingGame(parm1);
- if(pgame)
- {
- pgame->AddBlack(pInstance);
- plist=m_pending.FindItem(pgame);
- if(plist)
- m_pending.RemoveItem(plist);
- m_games.AddTail(pgame);
- _beginthread(GameLoop,0,pgame);
- return true;
- }
- else
- {
- pInstance->Print("No game found");
- }
- break;
- case QUIT:
- //user typed "quit"
- pInstance->Print("Good bye");
- pInstance->Close();
- delete pInstance;
- return true;
- default:
- //LBP (01/08/99): Print this chat to all players in the lobby.
- Chat(pInstance,buf);
- break;
- }
- return false;
- }
- //LBP (01/08/99): this is the lobby of sorts.
- void TalkProc(void* pParam)
- {
- log("TalkProc start");
- char* msg[]=
- {
- "Type 'create name' to make a game where 'name' is the game name you choose.rn" //0
- "Type 'join name' to join a player that created a game.rn"
- "Type 'watch name' to watch a game in progress",
- "Good bye.", //1
- "Syntax Error" //2
- };
- CPersonInstance* pInstance=(CPersonInstance*)pParam;
- char inbuf[CTelnetSocket::nSizeRecv];
- bool bAbort=false;
- m_connections.AddTail(pInstance);
- try
- {
- pInstance->Print(msg[0]);
- do
- {
- pInstance->Write("% ",2);
- pInstance->ReadLine(inbuf,sizeof inbuf,1800);
- if(ParsCmd(inbuf,pInstance))
- {
- bAbort=true;
- }
- } while(!bAbort);
- }
- catch(const char* e)
- {
- log(e);
- pInstance->Cleanup();
- delete pInstance;
- }
- log("TalkProc end");
- _endthread();
- }
- void AcceptProc(void* pParam)
- {
- CBlockingSocket* psockListen=(CBlockingSocket*)pParam;
- CSockAddr saClient;
- CPersonInstance sConnect;
- char* buffer;
- try
- {
- char* welcome="Welcome to the club.";
- char* nonuser="Sorry, members only";
- buffer=(char*)calloc(1,1000);
- assert(buffer);
- if(!psockListen->Accept(sConnect,saClient))
- _endthread();
- _beginthread(AcceptProc,0,pParam); //get ready for another connection
- log("Connection accepted");
-
- if(!Login(&sConnect))
- {
- sConnect.Print(nonuser);
- sConnect.Close();
- }
- else
- {
- sConnect.Print(welcome);
- //LBP (01/08/99): start up a lobby for this new connection
- CPersonInstance* newinstance=new CPersonInstance(sConnect);
- _beginthread(TalkProc,0,newinstance);
- }
- }
- catch(const char* e)
- {
- log(e);
- sConnect.Cleanup();
- }
- _endthread();
- }
- void CheckKey(void* dummy)
- {
- int a;
- cin >> a;
- repeat=0;
- }
- void ProcessGames()
- {
- _beginthread(CheckKey,0,NULL);
- do
- {
- } while(repeat);
- }
- void main(void)
- {
- CBlockingSocket sockListen;
- WSADATA wsd;
- if(WSAStartup(0x0101,&wsd)!=0)
- {
- cout << "Unable to start socketn";
- }
- else
- {
- cout << "Chess Server Demo by Lee Pattersonn";
- cout << "leepatterson@home.comnn";
- cout << "Demonstrates an object oriented server that can handlen";
- cout << "any number of chess games with 2 players and any number ofn";
- cout << "watchers per game.nn";
- cout << "Use telnet as a client connect to this server (ie: telnet localhost).n";
- cout << "Server uses port 23 (telnet default).n";
- cout << "Ctrl+c exits server";
- try
- {
- CSockAddr saServer(INADDR_ANY,23);
- sockListen.Create();
- sockListen.Bind(saServer);
- sockListen.Listen();
- _beginthread(AcceptProc,0,&sockListen);
- }
- catch(const char* e)
- {
- sockListen.Cleanup();
- cout << e << "n";
- }
- ProcessGames();
- try
- {
- sockListen.Close();
- Sleep(300);
- WSACleanup();
- }
- catch(const char* e)
- {
- cout << e << "n";
- }
- }
- }