320 likes | 532 Views
Game Networking 3 How to handle multiple clients. Bryan Duggan. Sockets. A software interface to the internet Create on and you can read and write data to/from nodes on the internet Supported on Windows: Version 1.0 1992 Version 1.1 1993 Version 2.0 1995 Sockets for IP v6 2001
E N D
Game Networking 3How to handle multiple clients Bryan Duggan
Sockets • A software interface to the internet • Create on and you can read and write data to/from nodes on the internet • Supported on Windows: • Version 1.0 1992 • Version 1.1 1993 • Version 2.0 1995 • Sockets for IP v6 2001 • Can be TCP, UDP • Can be blocking, non-blocking or asynchronous
UDP Sockets • Create your socket with SOCK_DGRAM • int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, socklen_t tolen); • int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
So far • If one client connects, the server is busy processing the client • It cant accept any more connections! • Multiple solutions to this: • Create a new process for each client • Create a new thread for each client • Listen on multiple sockets at once
Multi-tasking • Single tasking • MSDOS • Only one process at a time • Process must finish for another process to start • Co-operative • Windows 3.1 old Mac OS • Each process must relinquish control • A single process can lock up the whole system • Pre-emptive • Unix & Windows NT onwards (including Win95) • OS gives each running process a timeslice of the CPU • When the timeslice is up, the OS preempts the process
Multi-tasking • A program can create new processes • Unix • New process inherits the heap of the old process • Therefore has access to the host processes variables • Windows • New processes get their own heap • Only useful for starting a new program
Multithreading • Allows you to separate a program into concurrently running threads of execution • Bits of the program run at the same time • Threads of the same program share the same heap (global variables) • Each thread has its own stack (local variables) • Therefore you need thread synchronisation to prevent race conditions • The program must be multithreaded to take advantage of > 1 CPU
Creating a new thread HANDLE WINAPI CreateThread( __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTINE lpStartAddress, __in_opt LPVOID lpParameter, __in DWORD dwCreationFlags, __out_opt LPDWORD lpThreadId );
Example DWORD handleClient(LPVOID params) CreateThread(0, 0, (LPTHREAD_START_ROUTINE) handleClient, & clientSocket, 0, & threadID);
Sleeping • To sleep a thread: VOID WINAPI Sleep( __in DWORD dwMilliseconds );
Wait for a thread to complete • DWORD WINAPI WaitForSingleObject( __in HANDLE hHandle, __in DWORD dwMilliseconds ); • DWORD WINAPI WaitForMultipleObjects( __in DWORD nCount, __in const HANDLE* lpHandles, __in BOOL bWaitAll, __in DWORD dwMilliseconds );
A Thread class? • To create a thread in Java • Extend java.lang.Thread • Implement the function run() • To start the thread, call start() • How do we duplicate this functionality in C++
Problem! • CreateThread wont accept a member function as a parameter!! • It only will accept a static function or a plain old C function
A Virtual base class Thread: class Thread { protected: DWORD _threadID; bool _running; public: static DWORD threadStart(void * param); virtual void run(void) = 0; void start(); void stop(); };
DWORD Thread::threadStart(void *param) { ((Thread*)param)->run(); return 0; } void Thread::stop() { _running = false; } void Thread::start() { _running = true; CreateThread(0, 0, (LPTHREAD_START_ROUTINE) threadStart, this, 0, & _threadID); }
In run() • Periodically check the _running member variable to se if we should stop • You can kill a thread using: • BOOL WINAPI TerminateThread( __inout HANDLE hThread, __in DWORD dwExitCode ); • But you should try never to do this:
From MSDN: • If the target thread owns a critical section, the critical section will not be released. • If the target thread is allocating memory from the heap, the heap lock will not be released. • If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent. • If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.
Thread Priority • BOOL WINAPI SetThreadPriority( __in HANDLE hThread, __in int nPriority ); • BOOL WINAPI SetThreadPriority( __in HANDLE hThread, __in int nPriority );
Race Conditions • 2 threads access the same piece of global data, causing one thread to have out of date data • Can be difficult to debug and track down
Race condition! Thread 1 deletes all elements from _q, but s still holds 10 Example • Global variable: • vector<int> _q; • Say _q starts with 10 elements; • Thread 1: • int s = _q.size(); • _q.clear() • Thread 2: • int s = _q.size(); • for (i = 0 i; i < s ; i ++) • { • cout << _q[i]; • }
Synchronisation methods: • All implement efficient waiting • Critical sections • Mutex • Semaphores • Events
Critical Sections • Wrap code that should be executed synchronously in a critical section: • The code will block and wait if the program is in a critical section • Multiple threads can be waiting on a critical section (FIFO Q) • A thread cannot deadlock itself
Win32 API Calls void WINAPI InitializeCriticalSection( __out LPCRITICAL_SECTION lpCriticalSection ); void WINAPI EnterCriticalSection( __inout LPCRITICAL_SECTION lpCriticalSection ); void WINAPI LeaveCriticalSection( __inout LPCRITICAL_SECTION lpCriticalSection ); void WINAPI DeleteCriticalSection( __inout LPCRITICAL_SECTION lpCriticalSection );
MUTEX • Mutually exclusive access to a resource (can be code) • For example: • Global variable • Port • Printer • Threads own and release the mutex • Other threads wait for the mutex to be released
WIN32 API calls • DWORD WINAPI WaitForSingleObject( __in HANDLE hHandle, __in DWORD dwMilliseconds ); • BOOL WINAPI ReleaseMutex( __in HANDLE hMutex ); • HANDLE WINAPI OpenMutex( __in DWORD dwDesiredAccess, __in BOOL bInheritHandle, __in LPCTSTR lpName ); • HANDLE WINAPI CreateMutex( __in_opt LPSECURITY_ATTRIBUTES lpMutexAttributes, __in BOOL bInitialOwner, __in_opt LPCTSTR lpName );
Semaphores • A superset of a mutex • Has a counter associated with it • When the counter > 0 the semaphore is signalled and any wait function will proceed • When the counter = 0 wait functions block until counter > 0 • When you release you give an increment • Set the initial value when you create the semaphore
API calls • HANDLE WINAPI CreateSemaphore( __in_opt LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, __in LONG lInitialCount, __in LONG lMaximumCount, __in_opt LPCTSTR lpName ); • HANDLE WINAPI OpenSemaphore( __in DWORD dwDesiredAccess, __in BOOL bInheritHandle, __in LPCTSTR lpName ); • BOOL WINAPI ReleaseSemaphore( __in HANDLE hSemaphore, __in LONG lReleaseCount, __out_opt LPLONG lpPreviousCount );
Bounded buffers • Can be controlled with 2 semaphores • Empty – Starts at N • Full – Starts with 0 • Writer • Waits on empty • Releases full • Reader • Waits on full • Releases empty
Alternative to threads • Synchronous IO Multiplexing using: • select – Cross platform • WSASelect – Windows only
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); • FD_SET(int fd, fd_set *set); • Add fd to the set. • FD_CLR(int fd, fd_set *set); • Remove fd from the set. • FD_ISSET(int fd, fd_set *set); • Return true if fd is in the set. • FD_ZERO(fd_set *set); • Clear all entries from the set.
Timeouts • struct timeval { int tv_sec; // seconds int tv_usec; // microseconds };
Have a look at: • http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#select • For more information!