Section 23.8. ClientServer Tic-Tac-Toe Using a Multithreaded Server


23.8. Client/Server Tic-Tac-Toe Using a Multithreaded Server

We now present a networked version of the popular game Tic-Tac-Toe, implemented with stream sockets and client/server techniques. The program consists of a FrmTicTac-ToeServer application (Fig. 23.5) and a FrmTicTacToeClient application (Fig. 23.6). The FrmTicTacToeServer allows two FrmTicTacToeClients to connect to the server and play Tic-Tac-Toe against each other. Sample outputs appear in Fig. 23.6. When the server receives a client connection, lines 7079 of Fig. 23.5 create instances of class Player to process each client in a separate thread of execution. This enables the server to handle requests from both clients. The server assigns value "X" to the first client that connects (player "X" makes the first move), then assigns value "O" to the second client. Throughout the game, the server maintains information regarding the status of the board so that it can validate players' requested moves. However, neither the server nor the client can establish whether a player has won the gamein this application, method GameOver (lines 134137) always returns False. Each client maintains its own GUI version of the Tic-Tac-Toe board to display the game. The clients can place marks only in empty squares on the board. Class Square (Fig. 23.7) is used to define squares on the Tic-Tac-Toe board.

Figure 23.5. Server side of client/server Tic-Tac-Toe program.

  1  ' Fig. 23.5: FrmTicTacToeServer.vb  2  ' This class maintains a game of Tic-Tac-Toe for two  3  ' client applications.  4  Imports System.Net          5  Imports System.Net.Sockets  6  Imports System.Threading    7  Imports System.IO           8  9  Public Class FrmTicTacToeServer 10     Private board() As Byte  ' the local representation of the game board   11     Private players() As Player ' two Player objects                        12     Private playerThreads() As Thread ' Threads for client interaction      13     Private listener As TcpListener ' listen for client connection          14     Private currentPlayer As Integer ' keep track  of whose turn it is      15     Private getPlayers As Thread ' Thread for acquiring  client connections 16     Friend disconnected As Boolean = False  ' True  if the server closes    17 18     ' initialize variables and thread for receiving clients 19     Private Sub FrmTicTacToeServer_Load(ByVal sender As System.Object, _ 20        ByVal e As System.EventArgs) Handles MyBase .Load 21 22        board = New Byte(9) {}           23        players = New Player(2) {}       24        playerThreads = New Thread(2) {} 25        currentPlayer = 0                26 27        ' accept connections on a different thread                28        getPlayers = New Thread(New ThreadStart(AddressOf SetUp)) 29        getPlayers.Start()                                        30     End Sub ' FrmTicTacToeServer_Load 31 32     ' notify Players to stop Running 33     Private Sub FrmTicTacToeServer_FormClosing( _ 34        ByVal sender As System.Object, _ 35        ByVal e As System.Windows.Forms.FormClosingEventArgs) _ 36        Handles MyBase.FormClosing 37 38        disconnected = True                                  39        System.Environment.Exit(System.Environment.ExitCode) 40     End Sub ' FrmTicTacToeServer_FormClosing 41 42     ' Delegate that allows method DisplayMessage to be called 43     ' in the thread that creates and  maintains the GUI 44     Private Delegate Sub DisplayDelegate(ByVal message As String) 45 46     ' method DisplayMessage sets txtDisplay's Text property 47     ' in a thread-safe manner 48     Friend Sub DisplayMessage(ByVal message As String) 49        ' if modifying txtDisplay is not thread safe 50        If txtDisplay.InvokeRequired Then 51           ' use inherited method Invoke to execute DisplayMessage 52           ' via a Delegate 53           Invoke(New DisplayDelegate(AddressOf  DisplayMessage), _ 54              New Object() {message}) 55        ' OK to modify txtDisplay in current thread 56        Else 57           txtDisplay.Text &= message 58        End If 59     End Sub ' DisplayMessage 60 61     ' accepts connections from 2 players 62     Public Sub SetUp() 63        DisplayMessage("Waiting for players..."  & vbCrLf) 64 65        ' set up Socket                                                 66        listener = New TcpListener(IPAddress.Parse("localhost"), 50000) 67        listener.Start()                                                68 69        ' accept first player and start a player thread          70        players(0) = New Player(listener.AcceptSocket(), Me, 0) 71        playerThreads(0) = New Thread( _                        72           New ThreadStart(AddressOf players(0).Run))           73        playerThreads(0).Start()                                74 75        ' accept second player and start another player thread  76        players(1) = New Player(listener.AcceptSocket(), Me, 1) 77        playerThreads(1) = New Thread( _                        78           New ThreadStart(AddressOf players(1).Run))           79        playerThreads(1).Start()                                80 81        ' let the first player know that the other player has connected 82        SyncLock players(0)                                             83           players(0).threadSuspended = False                           84           Monitor.Pulse(players(0))                                    85        End SyncLock                                                    86     End Sub ' SetUp 87 88     ' determine if a move is valid 89     Public Function ValidMove(ByVal location  As Integer, _ 90        ByVal player As Integer) As Boolean 91        ' prevent another thread from making a move 92        SyncLock Me 93           ' while it is not the current player's turn, wait 94           While player <> currentPlayer 95              Monitor.Wait(Me)           96           End While                     97 98           ' if the desired square is not occupied 99           If Not IsOccupied(location) Then 100             ' set the board to contain the current player's mark 101             If currentPlayer = 0 Then                             102                board(location) = Convert.ToByte("X"c)            103             Else                                                 104                board(location) = Convert.ToByte("O"c)            105             End If                                               106 107             ' set the currentPlayer to be the other player 108             currentPlayer = (currentPlayer + 1) Mod 2      109 110             ' notify the other player of the move             111             players(currentPlayer).OtherPlayerMoved(location) 112 113             ' alert the other player that it's time to move 114             Monitor.Pulse(Me)                               115             Return True 116          Else 117             Return False 118          End If 119       End SyncLock 120    End Function ' ValidMove 121 122    ' determines whether the specified square is occupied 123    Public Function IsOccupied(ByVal location As Integer) As Boolean 124       If board(location) = Convert.ToByte("X"c) Or _ 125          board(location) = Convert.ToByte("O"c) Then 126 127          Return True 128       Else 129          Return False 130       End If 131    End Function ' IsOccupied 132 133    ' determines if the game is over 134    Public Function GameOver() As Boolean 135       ' place code here to test for a winner of the game 136       Return False 137    End Function ' GameOver 138  End Class ' FrmTicTacToeServer 139 140  ' class Player represents a tic-tac-toe player 141  Public Class Player 142     Friend connection As Socket ' Socket for accepting a connection          143     Private socketStream As NetworkStream ' network  data stream             144     Private server As FrmTicTacToeServer ' reference to server               145     Private writer As BinaryWriter ' facilitates writing to the stream       146     Private reader As BinaryReader ' facilitates reading from the stream     147     Private number As Integer ' player number                                148     Private mark As Char ' player's mark on the board                        149     Friend threadSuspended As Boolean = True ' if waiting   for other player 150 151     ' constructor requiring Socket, TicTacToeServerForm and Integer 152     ' objects as arguments 153     Public Sub New(ByVal socket As Socket, _ 154        ByVal serverValue As FrmTicTacToeServer, ByVal newNumber As Integer) 155 156        If newNumber = 0 Then 157           mark = "X"c 158        Else 159           mark = "O"c 160        End If 161 162        connection  = socket 163        server = serverValue 164        number = newNumber 165 166        ' create NetworkStream object for Socket    167        socketStream = NewNetworkStream(connection) 168 169        ' create Streams for reading/writing bytes 170        writer = New BinaryWriter(socketStream)    171        reader = New BinaryReader(socketStream)    172     End Sub ' New 173 174     ' signal other player of  move 175     Public Sub OtherPlayerMoved(ByVal location As Integer) 176        ' signal that opponent moved                    177        writer.Write("Opponent moved.")                 178        writer.Write(location) ' send  location of move 179     End Sub ' OtherPlayerMoved 180 181     ' allows the players to make moves and receive moves 182     ' from the other player 183     Public Sub Run() 184        Dim done As Boolean = False 185 186        ' display on the server that a connection was made 187        If number = 0 Then 188           server.DisplayMessage("Player X  connected" & vbCrLf) 189        Else 190          server.DisplayMessage("Player O  connected" & vbCrLf) 191        End If 192 193        ' send the current player's mark to the client 194        writer.Write(mark)                             195 196        ' if number equals 0 then this player is X,                   197        ' otherwise O must wait for X's first move                    198        If number = 0 Then                                            199           writer.Write("Player X  connected.")                       200        Else                                                          201           writer.Write("Player O  connected, please wait." & vbCrLf) 202        End If                                                        203 204        ' X must wait for another player to arrive 205        If mark = "X"c Then 206           writer.Write("Waiting  for another player.") 207 208           ' wait for notification  from server that another 209           ' player has connected 210           SyncLock Me               211               While threadSuspended 212                  Monitor.Wait(Me)   213               End While             214           End SyncLock              215 216           writer.Write("Other player connected.  Your move.") 217        End If 218 219        ' play game 220        While Not done 221           ' wait for data to become available 222           While connection.Available = 0 223              Thread.Sleep(1000) 224 225              If server.disconnected  Then 226                 Return 227              End If 228           End While 229 230           ' receive data                                231           Dim location As Integer  = reader.ReadInt32() 232 233           ' if the move is valid,  display the move on the 234           ' server and signal that the  move is valid 235           If server.ValidMove(location, number) Then 236              server.DisplayMessage("loc: " & location  & vbCrLf) 237              writer.Write("Valid move.") 238           ' signal that the move is invalid 239           Else 240              writer.Write("Invalid move, try again.") 241           End If 242 243           ' if game is over, set done to True to exit while loop 244           If server.GameOver()  Then 245              done = True 246           End If 247        End While 248 249        ' close the socket connection 250        writer.Close()                251        reader.Close()                252        socketStream.Close()          253        connection.Close()            254     End Sub  ' Run 255  End Class ' Player 

Figure 23.6. Client side of client/server Tic-Tac-Toe program.

  1  ' Fig. 23.6: FrmTicTacToeClient.vb  2  ' Client for the TicTacToe program.  3  Imports System.Drawing      4   Imports System.Net.Sockets  5  Imports System.Threading    6  Imports System.IO           7  8  Public Class FrmTicTacToeClient  9     Private board(,) As Square ' local representation of the game board    10     Private currentSquareValue As Square ' Square that this player chose   11     Private outputThread As Thread ' Thread for receiving data from server 12     Private connection As TcpClient ' client to establish connection       13     Private stream As NetworkStream ' network data stream                  14     Private writer As BinaryWriter ' facilitates writing to the stream     15     Private reader As BinaryReader ' facilitates reading from the stream   16     Private myMark As Char ' player's mark on the board                    17     Private myTurn As Boolean ' is it this player's turn?                  18     Private brush As SolidBrush ' brush for drawing X's and O's            19     Private done As Boolean = False ' True when game is over               20 21     ' initialize variables and thread for connecting to server 22     Private Sub FrmTicTacToeClient_Load(ByVal sender As System.Object, _ 23        ByVal e As System.EventArgs) Handles MyBase .Load 24 25        board = New Square(3, 3 ) {} 26 27        ' create 9 Square objects and place them on the board 28        board(0 , 0) = New Square(pnlBoard0, " "c , 0 )      29        board(0, 1) = New Square(pnlBoard1, " "c , 1 )      30        board(0, 2) = New Square(pnlBoard2, " "c , 2 )      31        board(1, 0) = New Square(pnlBoard3, " "c , 3 )      32        board(1, 1) = New Square(pnlBoard4, " "c , 4 )      33        board(1, 2) = New Square(pnlBoard5, " "c , 5 )      34        board(2, 0) = New Square(pnlBoard6, " "c , 6 )      35        board(2, 1) = New Square(pnlBoard7, " "c , 7 )      36        board(2, 2) = New Square(pnlBoard8, " "c , 8 )      37 38        ' create a SolidBrush for writing on the Squares 39        brush = New SolidBrush(Color.Black )             40 41        ' make connection to server and get the associated 42        ' network stream                                   43        connection = New TcpClient( "localhost" , 50000)   44        stream = connection.GetStream()                    45        writer = New BinaryWriter(stream)                  46        reader = New BinaryReader(stream)                  47 48        ' start a new thread for sending and receiving messages 49        outputThread = New Thread(New ThreadStart( AddressOf Run)) 50        outputThread.Start() 51     End Sub ' FrmTicTacToeClient_Load 52 53     ' repaint the Squares 54     Private Sub FrmTicTacToeClient_Paint( ByVal sender As System.Object, _ 55        ByVal e As System.Windows.Forms.PaintEventArgs) _ 56        Handles MyBase .Paint 57 58        PaintSquares() 59     End Sub ' FrmTicTacToeClient_Paint 60 61     ' game is over 62     Private Sub FrmTicTacToeClient_FormClosing( _ 63        ByVal sender As System.Object, _ 64        ByVal e As System.Windows.Forms.FormClosingEventArgs) _ 65        Handles MyBase.FormClosing 66 67        done = True                                          68        System.Environment.Exit(System.Environment.ExitCode) 69     End Sub ' FrmTicTacToeClient_FormClosing 70 71     ' Delegate that allows method  DisplayMessage to be called 72     ' in the thread that creates and  maintains the GUI 73     Private Delegate Sub DisplayDelegate(ByVal  message  As String) 74 75     ' method DisplayMessage sets txtDisplay's Text property 76     ' in a thread-safe  manner 77     Private Sub DisplayMessage(ByVal message As String) 78        ' if modifying txtDisplay is not thread safe 79        If txtDisplay.InvokeRequired Then 80           ' use inherited method Invoke to execute DisplayMessage 81           ' via a Delegate 82           Invoke(New DisplayDelegate(AddressOf DisplayMessage), _ 83              New Object () {message}) 84        ' OK to modify txtDisplay in current thread 85        Else 86           txtDisplay.Text &= message 87        End If 88     End Sub ' DisplayMessage 89 90     ' Delegate that allows method ChangeIdLabel to be called 91     ' in the thread that creates and maintains the GUI 92     Private Delegate Sub ChangeIdLabelDelegate(ByVal message As String) 93 94     ' method ChangeIdLabel sets txtDisplay's Text property 95     ' in a thread-safe manner 96     Private Sub ChangeIdLabel(ByVal label As String) 97        ' if modifying lblId is not thread safe 98        If lblId.InvokeRequired Then 99           ' use inherited method Invoke to execute ChangeIdLabel 100          ' via a Delegate 101          Invoke(New ChangeIdLabelDelegate(AddressOf ChangeIdLabel), _ 102             New Object () {label}) 103       ' OK to modify lblId in current thread 104       Else 105          lblId.Text = label 106       End If 107    End Sub ' ChangeIdLabel 108 109    ' draws the mark of each square 110    Public Sub PaintSquares() 111       Dim g As Graphics 112 113       ' draw the appropriate mark on each panel 114       For row As Integer = 0 To 2 115 116          For column As Integer = 0 To 2 117             ' get the Graphics for each Panel                   118             g = board(row, column).SquarePanel.CreateGraphics() 119 120             ' draw the appropriate letter on the panel         121             g.DrawString(board(row, column).Mark.ToString(), _ 122                pnlBoard0.Font, brush, 10, 8)                    123          Next column 124 125       Next row 126    End Sub ' PaintSquares 127 128    ' send location of the clicked square to server 129    Private Sub square_MouseUp(ByVal sender As System.Object, _ 130       ByVal e As System.Windows.Forms.MouseEventArgs) _ 131       Handles pnlBoard0.MouseUp, pnlBoard5.MouseUp, pnlBoard4.MouseUp, _ 132       pnlBoard3.MouseUp, pnlBoard2.MouseUp, pnlBoard1.MouseUp, _ 133       pnlBoard8.MouseUp, pnlBoard7.MouseUp, pnlBoard6.MouseUp 134 135       ' for each square check if that square was clicked 136       For row As Integer = 0 To 2 137 138          For column As Integer = 0 To 2 139            If board(row, column).SquarePanel.Equals(sender) Then 140                currentSquareValue = board(row, column) 141 142                ' send the move to the server                  143                SendClickedSquare(board(row, column).Location) 144             End If 145          Next column 146       Next row 147    End Sub ' square_MouseUp 148 149    ' control thread that allows continuous update of the 150    ' TextBox display 151    Public Sub Run() 152       myMark = reader.ReadChar() ' first get players's mark (X or O) 153       ChangeIdLabel("You are player """ & myMark & """")             154        155       If myMark = "X"c Then                                          156          myTurn = True                                               157       Else                                                           158          myTurn = False                                              159       End If                                                         160 161        ' process incom ing messages 162       Try 163          ' receive messages sent to client        164          While Not done                           165             ProcessMessage(reader.ReadString())   166          End While                                167       Catch ex As IOException 168          MessageBox.Show("Server is down, game over", "Error", _ 169             MessageBoxButtons.OK, MessageBoxIcon.Error ) 170       End Try 171    End Sub ' Run 172 173    ' process messages sent to client 174    Public Sub ProcessMessage(ByVal message As String) 175       ' if the move the player sent to the server is valid 176       ' update the display, set that square's mark to be 177       ' the mark of the current player and repaint the board 178       If message = "Valid move."  Then 179          DisplayMessage("Valid move, please wait." & vbCrLf ) 180          currentSquareValue.Mark = myMark 181          PaintSquares() 182       ElseIf  message = "Invalid move, try again."  Then 183         ' if the move is invalid, display that and it is now 184         ' this player's turn again 185         DisplayMessage(message & vbCrLf ) 186         myTurn = True 187      ElseIf  message = "Opponent moved."  Then 188         ' if opponent moved, find location of their move 189         Dim location As Integer = reader.ReadInt32()     190 191         ' set that square to have the opponents mark and     192         ' repaint the board                                  193         If myMark = "X"c Then                                194            board(location \ 3 , location Mod 3 ).Mark = "O"c 195         Else                                                 196            board(location \ 3 , location Mod 3 ).Mark = "X"c 197         End If                                               198         PaintSquares()                                       199 200         DisplayMessage("Opponent moved. Your turn." & vbCrLf ) 201 202         ' it is now this player's turn 203         myTurn = True 204      Else 205         DisplayMessage(message & vbCrLf ) ' display message 206      End If 207   End Sub ' ProcessMessage 208 209   ' sends the server the number of the clicked square 210   Public Sub SendClickedSquare(ByVal location As Integer ) 211      ' if it is the current player's move right now 212      If myTurn Then 213         ' send the location of the move to the server 214         writer.Write(location)                        215 216         ' it is now the other player's turn 217         myTurn = False 218      End If 219   End Sub ' SendClickedSquare 220 221   ' write-only property for the current square 222   Public WriteOnly Property CurrentSquare() As Square 223      Set(ByVal value As Square) 224          currentSquareValue = value 225       End Set 226    End Property ' CurrentSquare 227  End Class ' FrmTicTacToeClient 

At the start of the game.

(a)

(b)

Af ter Player X makes the first move.

(c)

(d)

After Player O makes the second move.

(e)

(f)

After Player X makes the final move.

(g)

(h)

The Tie Tac Toe Server's output from the client interactions.

(i)

Figure 23.7. Class Square.

  1  ' Fig. 23.7: Square.vb  2  ' A Square on the TicTacToe board.  3  4  ' the representation of a square in a tic-tac-toe  grid  5  Public Class Square  6     Private panel As Panel ' GUI  Panel that represents this Square  7     Private markValue As Char ' player's markValue  on this Square (if any)  8  9     ' locationValue on the board of this Square 10    Private locationValue As Integer 11 12    ' constructor 13    Public Sub New(ByVal newPanel As Panel, ByVal newMark As Char, _ 14       ByVal newLocation As Integer) 15 16       panel = newPanel 17       markValue = newMark 18       locationValue = newLocation 19    End Sub ' New 20 21    ' property SquarePanel; the panel which the square represents 22    Public ReadOnly Property SquarePanel() As Panel 23       Get 24          Return panel 25       End Get 26     End Property ' SquarePanel 27 28     ' property Mark; the markValue on the square 29     Public Property Mark() As Char 30        Get 31           Return markValue 32        End Get 33        Set(ByVal value As Char) 34           markValue = value 35        End Set 36     End Property ' Mark 37 38     ' property Location; the square's locationValue on the board 39     Public ReadOnly Property Location() As Integer 40        Get 41           Return locationValue 42        End Get 43     End Property ' Location 44  End Class ' Square 

FrmTicTacToeServer Class

FrmTicTacToeServer (Fig. 23.5) uses its Load event handler (lines 1930) to create a Byte array to store the moves the players have made (line 22). The program creates an array of two references to Player objects (line 23) and an array of two references to Thread objects (line 24). Each element in both arrays corresponds to a Tic-Tac-Toe player. Variable currentPlayer is set to 0 (line 25), which corresponds to player "X". In our program, player "X" makes the first move. Lines 2829 create and start Thread getPlayers, which the FrmTicTacToeServer uses to accept connections so that the current Thread does not block while awaiting players.

Lines 4459 define DisplayDelegate and DisplayMessage, allowing any thread to modify txtdisplay's Text property. This time, the DisplayMessage method is declared as Friend, so it can be called inside a method of class Player through a FrmTicTac-ToeServer reference. Classes in the same project can access each other's Friend members.

Thread getPlayers executes method SetUp (lines 6286), which creates a TcpListener object to listen for requests on port 50000 (lines 6667). This object then listens for connection requests from the first and second players. Lines 70 and 76 instantiate Player objects representing the players, and lines 7173 and 7779 create two Threads that execute the Run methods of each Player object.

The Player constructor (Fig. 23.5, lines 153172) receives as arguments a reference to the Socket object (i.e., the connection to the client), a reference to the FrmTicTac-ToeServer object and an Integer indicating the player number (from which the constructor infers the mark "X" or "O" used by that player). FrmTicTacToeServer calls method Run (lines 183254) after instantiating a Player object. Lines 187194 notify the server of a successful connection and send the client the Char that the client will place on the board when making a move. If Run is executing for Player "X", lines 205217 execute, causing Player "X" to wait for a second player to connect. Lines 211213 suspend the Player "X" Thread until the server signals that Player "O" has connected. The server notifies the Player of the connection by setting the Player's tHReadSuspended variable to False (line 83). When threadSuspended becomes False, Player exits the loop in lines 211213.

Lines 220247 in method Run enable the user to play the game. Each iteration of this statement waits for the client to send an Integer specifying where on the board to place the "X" or "O"the Player then places the mark on the board if the specified mark location is valid (i.e., if that location does not already contain a mark). Note that the loop continues execution only if Boolean variable done is False. This variable is set to true by event handler FrmTicTacToeServer_FormClosing of class FrmTicTacToeServer, which is invoked when the server closes the connection.

Line 222 of Fig. 23.5 begins a loop that iterates until Socket property Available indicates that there is information to receive from the Socket (or until the server disconnects from the client). If there is none, the THRead sleeps for one second. On awakening, the THRead uses property Disconnected to check whether server variable disconnected is true (line 225). If so, the Thread exits the method (thus terminating the Thread); otherwise, the Thread loops again. However, if property Available indicates that there is data to receive, the loop in lines 222228 terminates, enabling the information to be processed.

This information contains an Integer representing the location in which the client wants to place a mark. Line 231 calls method ReadInt32 of the BinaryReader object (which reads from the NetworkStream created with the Socket) to read this Integer. Line 235 then passes the Integer to FrmTicTacToeServer method ValidMove. If this method validates the move, the Player places the mark in the desired location.

Method ValidMove (lines 89120) sends the client a message indicating whether the move was valid. Locations on the board correspond to numbers from 0 to 8 (02 for the top row, 35 for the middle and 68 for the bottom). All the statements in the method are enclosed in a SyncLock statement so that only one move can be attempted at a time. This prevents two players from modifying the game's state information simultaneously. If the Player attempting to validate a move is not the current player (i.e., the one allowed to make a move), that Player is placed in a Wait state until its turn. If the user attempts to place a mark on a location that already contains a mark, method ValidMove returns False. However, if the user has selected an unoccupied location (line 99), lines 101105 place the mark on the local representation of the board. Line 111 notifies the other Player that a move has been made, and line 114 invokes the Pulse method so that the waiting Player can validate a move. The method then returns TRue to indicate that the move is valid.

When FrmTicTacToeClient (Fig. 23.6) executes, it creates a TextBox to display messages from the server and the Tic-Tac-Toe board representation. The board is created out of nine Square objects (Fig. 23.7) that contain Panels on which the user can click, indicating the position on the board in which to place a mark. FrmTicTacToeClient's Load event handler (lines 2251) opens a connection to the server (line 43) and obtains a reference to the connection's associated NetworkStream object from TcpClient (line 44). Lines 4950 start a thread to read messages sent from the server to the client. The server passes messages (e.g., whether each move is valid) to method ProcessMessage (lines 174207). If the message indicates that a move is valid (line 178), the client sets its Mark to the current square (the square that the user clicked) and repaints the board. If the message indicates that a move is invalid (line 182), the client notifies the user to click a different square. If the message indicates that the opponent made a move (line 187), line 189 reads an Integer from the server specifying where on the board the client should place the opponent's Mark. FrmTicTacToeClient includes a Delegate/method pair for allowing threads to modify lblId's Text property (lines 92107), as well as DisplayDelegate and DisplayMesage for modifying txtdisplay's Text property (lines 7388).



Visual BasicR 2005 for Programmers. DeitelR Developer Series
Visual Basic 2005 for Programmers (2nd Edition)
ISBN: 013225140X
EAN: 2147483647
Year: 2004
Pages: 435

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net