1 Chapter 20 – Networking Outline 20.1 20.2 20.3 20.4 20.5 20.6 20.7 Introduction Accessing URLs over HTTP Establishing a Simple Server (Using Stream Sockets) Establishing a Simple Client (Using Stream Sockets) Client/Server Interaction with Stream Socket Connections Connectionless Client/Server Interaction with Datagrams Client/Server Tic-Tac-Toe Using a Multithreaded Server 2002 Prentice Hall. All rights reserved. 2 20.1 Introduction • Client/server relationship • stream sockets provide connection-oriented using Transmission Control Protocol (TCP) • User Datagram Protocol (UDP) enables connectionless network communication with datagram sockets • Module socket contains the functions and class definitions that enable programs to communicate over a network 2002 Prentice Hall. All rights reserved. 3 20.2 Accessing URLs over HTTP • HyperText Transfer Protocol (HTTP), which is crucial to data transmission on the Web, uses Uniform (or Universal) Resource Locators (URLs) to locate content on the Internet • URLs represent files, directories or complex tasks, such as database lookups and Internet searches 2002 Prentice Hall. All rights reserved. 4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 Outline # Fig. 20.1: fig20_01.py # Displays the contents of a file from a Web server in a browser. Provides methods for accessing data referred to by URLs URLs from Tkinter import * Provides functions to parse and manipulate import Pmw import urllib Implements a simple Web browser import urlparse fig20_1.py class WebBrowser( Frame ): """A simple Web browser""" def __init__( self ): """Create the Web browser GUI""" Frame.__init__( self ) Pmw.initialise() self.pack( expand = YES, fill = BOTH ) self.master.title( "Simple Browser" EntryWeb component for) user-entered self.master.geometry( "400x300" ) URL self.address = Entry( self ) in =Pmw self.address.pack( fill Web = X, page padxdisplayed = 5, pady 5 )ScrolledText self.address.bind( "<Return>", self.getPage ) component self.contents = Pmw.ScrolledText( self, text_state = DISABLED ) self.contents.pack( expand = YES, fill = BOTH, padx = 5, and displays specified Web page pady =Retrieves 5 ) def getPage( self, event ): """Parse URL, add addressing scheme and retrieve file""" 2002 Prentice Hall. All rights reserved. 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 Retrieve Takes URL from Entry component a string as input and returns a six-element tuple # parse the URL myURL = event.widget.get() components = urlparse.urlparse( myURL ) self.contents.text_state = NORMAL 5 Outline fig20_1.py FirstAppend elementhttp:// of tuple returned by urlparse.urlparse is addressing scheme to user-entered URL if necessary # if addressing scheme not specified, use http if components[ 0 ] == "": myURL = "http://" + myURL # connect and retrieve the file Retrieve requested file try: Display requested file tempFile = urllib.urlopen( myURL ) self.contents.settext( tempFile.read() ) # show results Generated if file could not be found tempFile.close() except IOError: self.contents.settext( "Error finding file" ) self.contents.text_state = DISABLED def main(): WebBrowser().mainloop() if __name__ == "__main__": main() 2002 Prentice Hall. All rights reserved. 6 Outline fig20_1.py 2002 Prentice Hall. All rights reserved. 7 20.3 Establishing a Simple Server (Using Stream Sockets) • Step 1: Create a socket object with a call to the socket constructor: socket.socket( family, type ) – family (AF_INET or AF_UNIX) specifies how to interpret any addresses used by the socket – type (SOCK_STREAM or SOCK_DGRAM) specifies stream sockets or datagram sockets, respectively • Step 2: Bind (assign) the socket object to a specified address: socket.bind( address ) – For AF_INET family sockets, address specifies the machine’s hostname or IP address and a port number for connections 2002 Prentice Hall. All rights reserved. 8 20.3 Establishing a Simple Server (Using Stream Sockets) • Step 3: Prepare socket to receive connection requests: socket.listen( backlog ) – backlog specifies the maximum number of clients that can request connections to the server • Step 4: Wait for a client to request a connection: connection, address = socket.accept() – socket object blocks indefinitely – Server communicates with client using connection, a new socket object – address corresponds to the client’s Internet address 2002 Prentice Hall. All rights reserved. 9 20.3 Establishing a Simple Server (Using Stream Sockets) • Step 5: Processing phase in which server and client communicates using socket methods send and recv • Step 6: Transmission completes and server closes connection by invoking close method on the socket object 2002 Prentice Hall. All rights reserved. 10 20.4 Establishing a Simple Client (Using Stream Sockets) • Step 1: Create a socket object • Step 2: Connect to the server using socket.connect( ( host, port ) ) where host represents server’s hostname or IP address and port is the port number to which the server process is bound • Step 3: Processing phase in which client and server communicate with methods send and recv • Step 4: Client closes connection by invoking close method on the socket object 2002 Prentice Hall. All rights reserved. 11 20.5 Client/Server Interaction with Stream Socket Connections • Implementation of a simple client/server chat application using stream sockets 2002 Prentice Hall. All rights reserved. 12 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 # # # # Fig. 20.2: fig20_02.py Set up a server that will receive a connection from a client, send a string to the client, and close the connection. import socket Outline fig20_02.py String “127.0.0.1” always refers to the local machine HOST = "127.0.0.1" PORT = 5000 counter = 0 Creates socket object mySocket to wait for connection requests # step 1: create socket mySocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) mySocket to port 5000 # step 2: bind theBind socket to address Method bind failure generates try: mySocket.bind( ( HOST, PORT ) ) except socket.error: print "Call to bind failed" socket.error exception while 1: Specifies that only one client can request a connection # step 3: wait for connection request print "Waiting for connection" Establishes connection for a request mySocket.listen( 1 ) # step 4: establish connection for request connection, address = mySocket.accept() counter += 1 print "Connection", counter, "received from:", address[ 0 ] Send data via connection Specify that server can receive at most 1024 bytes # step 5: send and receive data via connection connection.send( "SERVER>>> Connection successful" ) clientMessage = connection.recv( 1024 ) 2002 Prentice Hall. All rights reserved. 13 36 37 38 39 40 41 42 43 44 45 46 47 48 while clientMessage != "CLIENT>>> TERMINATE": if not clientMessage: break Outline fig20_02.py print clientMessage serverMessage = raw_input( "SERVER>>> " ) connection.send( "SERVER>>> " + serverMessage ) clientMessage = connection.recv( 1024 ) connection # step 6: close Terminate connection print "Connection terminated" connection.close() 2002 Prentice Hall. All rights reserved. 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 # Fig. 20.3: fig20_03.py # Set up a client that will read information sent # from a server and display that information. Outline fig20_03.py import socket HOST = "127.0.0.1" PORT = 5000 # step 1: create socket Create a stream socket print "Attempting connection" mySocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) Make a connection # step 2: make connection request torequest serverto try: mySocket.connect( ( HOST, PORT ) ) except socket.error: print "Call to connect failed" print "Connected to Server" Accept server message from server # step 3: transmit data via connection serverMessage = mySocket.recv( 1024 ) while serverMessage != "SERVER>>> TERMINATE": if not serverMessage: break Send message to server print serverMessage clientMessage = raw_input( "CLIENT>>> " ) mySocket.send( "CLIENT>>> " + clientMessage ) serverMessage = mySocket.recv( 1024 ) 2002 Prentice Hall. All rights reserved. 35 36 37 Terminate connection connection # step 4: close print "Connection terminated" mySocket.close() 15 Outline fig20_03.py Waiting for connection Connection 1 received from: 127.0.0.1 Attempting connection Connected to Server SERVER>>> Connection successful CLIENT>>> Hi to person at server Attempting Waiting forconnection connection Connectionto Connected 1 Server received from: 127.0.0.1 CLIENT>>> Connection SERVER>>> Hi to person successful at server CLIENT>>> person at server SERVER>>> Hi to back to you--client! SERVER>>> Hi back to you--client! CLIENT>>> TERMINATE Waiting for connection Connection 1 received from: 127.0.0.1 CLIENT>>> Hi to person at server SERVER>>> Hi back to you--client! Connection terminated Waiting for connection 2002 Prentice Hall. All rights reserved. 16 20.6 Connectionless Client/Server Interaction with Datagrams • Connectionless transmission uses datagrams (packets) • Connectionless transmission can result in lost, duplicated and lost packets 2002 Prentice Hall. All rights reserved. 17 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Outline # Fig. 20.4: fig20_04.py # Set up a server that will receive packets from a # client and send packets to a client. fig20_04.py import socket HOST = "127.0.0.1" PORT = 5000 Create a datagram socket object # step 1: create socket mySocket = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) Assign socket to specified host at port 5000 # step 2: bind socket mySocket.bind( ( HOST, PORT ) ) while 1: Method recvfrom blocks until message arrives Returns received message and address of the socket sending the data # step 3: receive packet packet, address = mySocket.recvfrom( 1024 ) print print print print print print "Packet received:" "From host:", address[ 0 ] "Host port:", address[ 1 ] "Length:", len( packet ) "Containing:" "\t" + packet Method sendto specifies data to be sent and the address to which it is sent # step 4: echo packet back to client print "\nEcho data to client...", mySocket.sendto( packet, address ) Terminate connection print "Packet sent\n" mySocket.close() 2002 Prentice Hall. All rights reserved. 18 Packet received: From host: 127.0.0.1 Host port: 1645 Length: 20 Containing: first message packet Outline fig20_04.py Echo data to client... Packet sent 2002 Prentice Hall. All rights reserved. 19 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # Fig. 20.5: fig20_05.py # Set up a client that will send packets to a # server and receive packets from a server. Outline fig20_05.py import socket HOST = "127.0.0.1" PORT = 5000 Create datagram socket # step 1: create socket mySocket = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) while 1: # step 2: send packet Send packet to HOST at PORT packet = raw_input( "Packet>>>" ) print "\nSending packet containing:", packet mySocket.sendto( packet, ( HOST, PORT ) ) print "Packet sent\n" Receive packet from server # step 3: receive packet back from server packet, address = mySocket.recvfrom( 1024 ) print print print print print print "Packet received:" "From host:", address[ 0 ] "Host port:", address[ 1 ] "Length:", len( packet ) "Containing:" Terminate connection "\t" + packet + "\n" mySocket.close() 2002 Prentice Hall. All rights reserved. 20 Packet>>>first message packet Sending packet containing: first message packet Packet sent Outline fig20_05.py Packet received: From host: 127.0.0.1 Host port: 5000 Length: 20 Containing: first message packet Packet>>> 2002 Prentice Hall. All rights reserved. 21 20.7 Client/Server Tic-Tac-Toe Using a Multithreaded Server • Tic-Tac-Toe game implemented with stream sockets and using client/server techniques • A TicTacToeServer class allows two TicTacToeClients to connect to the server and play the game 2002 Prentice Hall. All rights reserved. 22 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 # Fig. 20.6: fig20_06.py # Class TicTacToeServer maintains a game of Tic-Tac-Toe # for two clients, each managed by a Player thread. import socket Thread import threading Outline fig20_06.py to manage each Tic-Tac-Toe client individually class Player( threading.Thread ): """Thread to manage each Tic-Tac-Toe client individually""" def __init__( self, connection, server, number ): """Initialize thread and setup variables""" threading.Thread.__init__( self ) Specify each player’s mark # specify player's mark if number == 0: self.mark = "X" else: self.mark = "O" self.connection = connection self.server = server Send message to other self.number = number client that opponent moved def otherPlayerMoved( self, location ): """Notify client of opponent’s last move""" self.connection.send( "Opponent moved." ) self.connection.send( str( location ) ) def run( self ): """Play the game""" 2002 Prentice Hall. All rights reserved. 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 Each client receives message 0) indicating its mark (X or O) indicating its mark (X or Outline # send client message self.server.display( "Player %s connected." % self.mark ) self.connection.send( self.mark ) # wait for another player to arrive Wait for second player to arrive if self.mark == "X": self.connection.send( "Waiting for another player..." ) self.server.gameBeginEvent.wait() self.connection.send( "Other player connected. Your move." ) else: self.server.gameBeginEvent.wait() # wait for server self.connection.send( "Waiting for first move..." ) fig20_06.py Continue game until game is over # play game until over while not self.server.gameOver(): Get selected location from client # get more location from client location = self.connection.recv( 1 ) if not location: break # check for valid move Send message informing player whether if self.server.validMove( int( location ), self.number ): self.server.display( "loc: " + location ) self.connection.send( "Valid move." ) else: self.connection.send( "Invalid move, try again." ) selected move was valid Terminate connection # close connection to client self.connection.close() self.server.display( "Game over." ) self.server.display( "Connection closed." ) 2002 Prentice Hall. All rights reserved. 23 24 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 Outline class TicTacToeServer: """Server that maintains a game of Tic-Tac-Toe for two clients""" Server that maintains Tic-Tac-Toe game for two clients def __init__( self ): """Initialize variables and setup server""" fig20_06.py HOST = "" PORT = 5000 self.board = [] Condition variable controls which player moves Event object controls whether game has begun self.currentPlayer = 0 self.turnCondition = threading.Condition() self.gameBeginEvent = threading.Event() for i in range( 9 ): self.board.append( None ) Create server stream socket # setup server socket Bind server socket self.server = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) self.server.bind( ( HOST, PORT ) ) self.display( "Server awaiting connections..." ) def execute( self ): """Play the game--create and start both Player threads""" self.players = [] # wait for and accept Server two client socketconnections prepares for two connection requests Socket accepts a connection for i in range( 2 ): self.server.listen( 2 ) connection, address = self.server.accept() 2002 Prentice Hall. All rights reserved. 25 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 # assign each client to a Player thread self.players.append( Player( connection, self, i ) ) self.players[ Terminate -1 ].start() connection self.server.close() Outline Assign each client to a Player thread fig20_06.py # no more connections to wait for # players are suspended until player O connects Resume players # resume players now self.gameBeginEvent.set() def display( self, message ): """Display a message on the server""" print message Make valid move def validMove( self, location, player ): """Determine if a move is valid--if so, make move""" Acquire lock to ensure that only one move can be made at a given time # only one move can be made at a time self.turnCondition.acquire() Player waits for turn # while not current player, must wait for turn while player != self.currentPlayer: self.turnCondition.wait() Make move if location is not occupied # make move if location is not occupied if not self.isOccupied( location ): # set move on board if self.currentPlayer == 0: self.board[ location ] = "X" else: self.board[ location ] = "O" 2002 Prentice Hall. All rights reserved. 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 Change current player # change current player self.currentPlayer = ( self.currentPlayer + 1 ) % 2 self.players[ self.currentPlayer ].otherPlayerMoved( location ) Tell waiting player to continue 26 Outline fig20_06.py # tell waiting player to continue self.turnCondition.notify() self.turnCondition.release() # valid move return 1 # invalid move else: self.turnCondition.notify() self.turnCondition.release() return 0 def isOccupied( self, location ): """Determine if a space is occupied""" return self.board[ location ] # an empty space is None def gameOver( self ): """Determine if the game is over""" # place code here testing for a game winner # left as an exercise for the reader return 0 def main(): TicTacToeServer().execute() if __name__ == "__main__": main() 2002 Prentice Hall. All rights reserved. 27 Server awaiting connections... Player X connected. Player O connected. loc: 0 loc: 4 loc: 3 loc: 1 loc: 7 loc: 5 loc: 2 loc: 8 loc: 6 Outline fig20_06.py 2002 Prentice Hall. All rights reserved. 28 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 # Fig. 20.7: fig20_07.py # Client for Tic-Tac-Toe program. import socket import threading from Tkinter import * import Pmw Outline fig20_07.py Client that plays Tic-Tac-Toe class TicTacToeClient( Frame, threading.Thread ): """Client that plays a game of Tic-Tac-Toe""" Create GUI to play game def __init__( self ): """Create GUI and play game""" threading.Thread.__init__( self ) # initialize GUI Frame.__init__( self ) Pmw.initialise() self.pack( expand = YES, fill = BOTH ) self.master.title( "Tic-Tac-Toe Client" ) self.master.geometry( "250x325" ) self.id = Label( self, anchor = W ) self.id.grid( columnspan = 3, sticky = W+E+N+S ) self.board = [] Create and add all buttons to the board # create and add all buttons to the board for i in range( 9 ): newButton = Button( self, font = "Courier 20 bold", height = 1, width = 1, relief = GROOVE, Associate each button with an event – left name = str( i ) ) newButton.bind( "<Button-1>", self.sendClickedSquare ) self.board.append( newButton ) mouse button click 2002 Prentice Hall. All rights reserved. 29 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 Outline current = 0 # display buttons in 3x3 grid beginning with grid's row one for i in range( 1, 4 ): fig20_07.py for j in range( 3 ): self.board[ current ].grid( row = i, column = j, sticky = W+E+N+S ) current += 1 # area for server messages self.display = Pmw.ScrolledText( self, text_height = 10, text_width = 35, vscrollmode = "static" ) self.display.grid( row = 4, columnspan = 3 ) self.start() # run thread def run( self ): """Control thread to allow continuous updated display""" # setup connection to server HOST = "127.0.0.1" Create client socket PORT = 5000 self.connection = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) self.connection.connect( ( HOST, PORT ) ) # first get player s mark ( X or O ) self.myMark = self.connection.recv( 1 ) self.id.config( text = 'You are player "%s"' % self.myMark ) self.myTurn = 0 2002 Prentice Hall. All rights reserved. 30 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 # receive messages sent to client while 1: message = self.connection.recv( 34 ) Receive messages sent to client Outline fig20_07.py if not message: break self.processMessage( message ) Terminate connection self.connection.close() self.display.insert( END, "Game over.\n" ) self.display.insert( END, "Connection closed.\n" ) Interpret messages to perform necessary self.display.yview( ENDserver ) actions def processMessage( self, message ): """Interpret server message to perform necessary actions""" # valid move occurred if message == "Valid move.": self.display.insert( END, "Valid move, please wait.\n" ) self.display.yview( END ) # set mark self.board[ self.currentSquare ].config( text = self.myMark, bg = "white" ) # invalid move occurred elif message == "Invalid move, try again.": self.display.insert( END, message + "\n" ) self.display.yview( END ) self.myTurn = 1 # opponent moved elif message == "Opponent moved.": 2002 Prentice Hall. All rights reserved. 31 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 # get move location location = int( self.connection.recv( 1 ) ) # update board if self.myMark == "X": self.board[ location ].config( text = "O", bg = "gray" ) else: self.board[ location ].config( text = "X", bg = "gray" ) Outline fig20_07.py self.display.insert( END, message + " Your turn.\n" ) self.display.yview( END ) self.myTurn = 1 # other player's turn elif message == "Other player connected. Your move.": self.display.insert( END, message + "\n" ) self.display.yview( END ) self.myTurn = 1 # simply display message else: self.display.insert( END, message + "\n" ) self.display.yview( END ) Send attempted move to server def sendClickedSquare( self, event ): """Send attempted move to server""" if self.myTurn: name = event.widget.winfo_name() self.currentSquare = int( name ) 2002 Prentice Hall. All rights reserved. 32 138 139 140 141 142 143 144 145 146 # send location to server self.connection.send( name ) self.myTurn = 0 Send location of move to server def main(): TicTacToeClient().mainloop() Outline fig20_07.py if __name__ == "__main__": main() 2002 Prentice Hall. All rights reserved. 33 Outline fig20_07.py 2002 Prentice Hall. All rights reserved.
© Copyright 2026 Paperzz