This article mainly shares the sample program code of the PythonSocket programming chat room. Interested friends can refer to the previous article to learn simple Python TCP Socket programming, you can understand the basic Python Socket programming model by writing the code of the server and client respectively. This article uses an example to enhance the understanding of Socket programming.
I. Chat Room program Requirements
We want to implement an example of a simple chat room, that is, to allow multiple people to chat together at the same time, and everyone can receive the message sent by each person, similar to the QQ group function, instead of chatting between point-to-point QQ friends. For example:
We need to implement two parts:
- Chat Server:The Chat Server establishes a Socket connection with the user and broadcasts messages sent by a user to all online users.
- Telnet Client:The user chat client can enter and send the chat content, and display the message records of other users.
Similarly, we use TCP connections for message communication to ensure reliability. Before designing programs for the server and client, you must first learn a function called select to Implement Asynchronous I/O in Python.
Ii. Python asynchronous I/O
Python provides Asynchronous I/O (Asynchronous I/O) IN THE select module, which is similar to the select mechanism in Linux, but is simplified. First, I will introduce the select statement and then show you how to use it in Python.
The preceding article uses multithreading to concurrently process multiple socket I/O. The select method described here allows you to respond to multiple events of different sockets and other different events. For example, you can let the select statement notify you when a socket has data arriving, when a socket can write data, or when a socket has an error, the advantage is that you can respond to multiple socket events at the same time.
In Linux, the select statement in the C language uses the in-place diagram to indicate the file descriptor events we want to pay attention to. In Python, the list is used to indicate the file descriptor we monitor. When an event arrives, the returned list of file descriptors indicates that these files have events. The following simple program is to wait for the input to be obtained from the standard input:
rlist, wlist, elist = select.select( [sys.stdin], [], [] )print sys.stdin.read()
The three parameters of the select method are of the list type, which respectively represent read events, write events, and error events. The return values of the same method are also three lists, what events (read, write, and exception) are included. In the preceding example, the parameter has only one event sys. stdin indicates that only standard input events are concerned. Therefore, when a select statement is returned, rlist will only be [sys. stdin] indicates that data can be read from stdin. We use the read method to read data.
Of course, select is also valid for socket descriptors. The following example creates two socket clients to connect to the remote server. select is used to monitor which socket has data arrival:
import socketimport selectsock1 = socket.socket( socket.AF_INET, socket.SOCK_STREAM )sock2 = socket.socket( socket.AF_INET, socket.SOCK_STREAM )sock1.connect( ('192.168.1.1', 25) )sock2.connect( ('192.168.1.1', 25) )while 1: # Await a read event rlist, wlist, elist = select.select( [sock1, sock2], [], [], 5 ) # Test for timeout if [rlist, wlist, elist] == [ [], [], [] ]: print "Five seconds elapsed.\n" else: # Loop through each socket in rlist, read and print the available data for sock in rlist: print sock.recv( 100 )
Well, with the above foundation, we can design the server and client of the chat room.
Iii. Chat Room servers
The chat room server mainly performs the following two tasks:
- Receive connections from multiple clients
- Read the message sickness broadcast from each client to other connected clients
We define a list-type variable CONNECTION_LIST to listen to readable events of multiple socket events. The code for processing multiplexing I/O using the select statement described above is as follows:
# Get the list sockets which are ready to be read through selectread_sockets,write_sockets,error_sockets = select.select(CONNECTION_LIST,[],[])
When a select statement is returned, it indicates that there is readable data on read_sockets. There are two types of data:
1. If the master socket (that is, the socket created by the server, which has been in the listening status) has data readable, it indicates that new connection requests can be received, in this case, you need to call the accept function to receive new client connections and broadcast the connection information to other clients.
2. If other sockets (that is, sockets connected to the client) have readable data, it indicates that the client sends a message to the server and uses the recv function to read the message, and forward the message to all other connected clients.
In the above two cases, the process involves the process of broadcasting messages. Broadcasting means that messages obtained from a socket are sent one by one through each socket of CONNECTION_LIST (except itself and the main socket:
def broadcast_data (sock, message): #Do not send the message to master socket and the client who has send us the message for socket in CONNECTION_LIST: if socket != server_socket and socket != sock : try : socket.send(message) except : # broken socket connection may be, chat client pressed ctrl+c for example socket.close() CONNECTION_LIST.remove(socket)
If the sending fails, we assume that a client is disconnected and closes the socket and deletes it from the connection list.
The source code of the complete chat room server is as follows:
# Tcp Chat server import socket, select #Function to broadcast chat messages to all connected clientsdef broadcast_data (sock, message): #Do not send the message to master socket and the client who has send us the message for socket in CONNECTION_LIST: if socket != server_socket and socket != sock : try : socket.send(message) except : # broken socket connection may be, chat client pressed ctrl+c for example socket.close() CONNECTION_LIST.remove(socket) if __name__ == "__main__": # List to keep track of socket descriptors CONNECTION_LIST = [] RECV_BUFFER = 4096 # Advisable to keep it as an exponent of 2 PORT = 5000 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # this has no effect, why ? server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind(("0.0.0.0", PORT)) server_socket.listen(10) # Add server socket to the list of readable connections CONNECTION_LIST.append(server_socket) print "Chat server started on port " + str(PORT) while 1: # Get the list sockets which are ready to be read through select read_sockets,write_sockets,error_sockets = select.select(CONNECTION_LIST,[],[]) for sock in read_sockets: #New connection if sock == server_socket: # Handle the case in which there is a new connection recieved through server_socket sockfd, addr = server_socket.accept() CONNECTION_LIST.append(sockfd) print "Client (%s, %s) connected" % addr broadcast_data(sockfd, "[%s:%s] entered room\n" % addr) #Some incoming message from a client else: # Data recieved from client, process it try: #In Windows, sometimes when a TCP program closes abruptly, # a "Connection reset by peer" exception will be thrown data = sock.recv(RECV_BUFFER) if data: broadcast_data(sock, "\r" + '<' + str(sock.getpeername()) + '> ' + data) except: broadcast_data(sock, "Client (%s, %s) is offline" % addr) print "Client (%s, %s) is offline" % addr sock.close() CONNECTION_LIST.remove(sock) continue server_socket.close()
Run the program on the console:
$ python chat_server.py Chat server started on port 5000
Iv. Chat Room Client
We can write a client program to connect to the above server to complete the process of sending and receiving messages. We mainly do the following two things:
- Whether messages are sent from the listener server
- Check user input. If a user inputs a message, it must be sent to the server.
There are two I/O events that need to be monitored: socket connected to the server and standard input. We can also use select to do this:
rlist = [sys.stdin, s] # Get the list sockets which are readableread_list, write_list, error_list = select.select(rlist , [], [])
The logic is simple. If it is sys. stdin has readable data, indicating that the user enters data from the console and press enter, then the data is read from the standard input and sent to the server; if the socket connected to the server has readable data, indicates that the server sends a message to the client, and the data is received from the socket. The complete client code with some prompts and exception handling is as follows:
# telnet program exampleimport socket, select, string, sys def prompt() : sys.stdout.write('
') sys.stdout.flush() #main functionif __name__ == "__main__": if(len(sys.argv) < 3) : print 'Usage : python telnet.py hostname port' sys.exit() host = sys.argv[1] port = int(sys.argv[2]) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(2) # connect to remote host try : s.connect((host, port)) except : print 'Unable to connect' sys.exit() print 'Connected to remote host. Start sending messages' prompt() while 1: rlist = [sys.stdin, s] # Get the list sockets which are readable read_list, write_list, error_list = select.select(rlist , [], []) for sock in read_list: #incoming message from remote server if sock == s: data = sock.recv(4096) if not data : print '\nDisconnected from chat server' sys.exit() else : #print data sys.stdout.write(data) prompt() #user entered a message else : msg = sys.stdin.readline() s.send(msg) prompt()
You can run this code on multiple terminals:
$ python telnet.py localhost 5000Connected to remote host. Start sending messages
hello
I am fine<('127.0.0.1', 38378)> ok good
Information displayed on another terminal:
[127.0.0.1:39339] entered room<('127.0.0.1', 39339)> hello<('127.0.0.1', 39339)> I am fine
ok good
Summary
Note the following two points for the above Code:
1. The client code of the chat room cannot be run in windows, because the Code uses select to listen to both socket and input streams. In Windows, the select function is provided by the WinSock library, file descriptors not defined by WinSock cannot be processed.
2. The client code also has a defect: when a client inputs a message but has not yet sent it, the server also sends the message, which will erase the message being input by the client. This cannot be solved at present. The only solution is to use a terminal library like ncurses to enable independent user input and output, or write a GUI program.
This article uses the example of a chat room to further learn Socket programming in Python.