Vui lòng truy cập /cplusplus/sockets_server_client. php cho khái niệm chung về lập trình Mạng, TCP/IP/, ổ cắm, v.v.
Để biết thêm các mẫu đơn giản hơn, vui lòng truy cập chương máy chủ/máy khách cơ bản. Lập trình mạng Python
Trong chương này, chúng ta sẽ tạo một máy chủ trò chuyện. Máy chủ giống như một người trung gian giữa các khách hàng. Nó có thể xếp hàng lên đến 10 khách hàng. Máy chủ phát bất kỳ tin nhắn nào từ máy khách đến những người tham gia khác. Vì vậy, máy chủ cung cấp một loại phòng trò chuyện
Trong đoạn mã trò chuyện này, máy chủ đang xử lý các ổ cắm ở chế độ không chặn bằng cách chọn. phương thức chọn []
ready_to_read, ready_to_write, in_error = \ select.select[ potential_readers, potential_writers, potential_errs, timeout]
Chúng tôi vượt qua select[] ba danh sách
- cái đầu tiên chứa tất cả các ổ cắm mà chúng tôi có thể muốn thử đọc
- thứ hai là tất cả các ổ cắm mà chúng tôi có thể muốn thử ghi vào
- những cái cuối cùng [thường để trống] mà chúng tôi muốn kiểm tra lỗi
Mặc dù bản thân select[] là một cuộc gọi chặn [nó đang chờ hoàn thành I/O], chúng ta có thể cho nó thời gian chờ. Trong mã, chúng tôi đặt time_out = 0 và nó sẽ thăm dò ý kiến và không bao giờ chặn
Trên thực tế, chức năng select[] giám sát tất cả các ổ cắm máy khách và ổ cắm máy chủ để biết hoạt động có thể đọc được. Nếu bất kỳ ổ cắm máy khách nào có thể đọc được thì điều đó có nghĩa là một trong những máy khách trò chuyện đã gửi tin nhắn
Khi hàm select trả về, ready_to_read sẽ chứa đầy một mảng bao gồm tất cả các bộ mô tả ổ cắm có thể đọc được
Trong mã, chúng ta đang giải quyết hai trường hợp
- Nếu ổ cắm chính có thể đọc được, máy chủ sẽ chấp nhận kết nối mới
- Nếu bất kỳ ổ cắm máy khách nào có thể đọc được, máy chủ sẽ đọc tin nhắn và phát lại cho tất cả các máy khách ngoại trừ người gửi tin nhắn
Đây là mã máy chủ. chat_server. py
# chat_server.py import sys import socket import select HOST = '' SOCKET_LIST = [] RECV_BUFFER = 4096 PORT = 9009 def chat_server[]: server_socket = socket.socket[socket.AF_INET, socket.SOCK_STREAM] server_socket.setsockopt[socket.SOL_SOCKET, socket.SO_REUSEADDR, 1] server_socket.bind[[HOST, PORT]] server_socket.listen[10] # add server socket object to the list of readable connections SOCKET_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 # 4th arg, time_out = 0 : poll and never block ready_to_read,ready_to_write,in_error = select.select[SOCKET_LIST,[],[],0] for sock in ready_to_read: # a new connection request recieved if sock == server_socket: sockfd, addr = server_socket.accept[] SOCKET_LIST.append[sockfd] print "Client [%s, %s] connected" % addr broadcast[server_socket, sockfd, "[%s:%s] entered our chatting room\n" % addr] # a message from a client, not a new connection else: # process data recieved from client, try: # receiving data from the socket. data = sock.recv[RECV_BUFFER] if data: # there is something in the socket broadcast[server_socket, sock, "\r" + '[' + str[sock.getpeername[]] + '] ' + data] else: # remove the socket that's broken if sock in SOCKET_LIST: SOCKET_LIST.remove[sock] # at this stage, no data means probably the connection has been broken broadcast[server_socket, sock, "Client [%s, %s] is offline\n" % addr] # exception except: broadcast[server_socket, sock, "Client [%s, %s] is offline\n" % addr] continue server_socket.close[] # broadcast chat messages to all connected clients def broadcast [server_socket, sock, message]: for socket in SOCKET_LIST: # send the message only to peer if socket != server_socket and socket != sock : try : socket.send[message] except : # broken socket connection socket.close[] # broken socket, remove it if socket in SOCKET_LIST: SOCKET_LIST.remove[socket] if __name__ == "__main__": sys.exit[chat_server[]]
Có sự mơ hồ về cách chúng tôi phát hiện xem kết nối có bị hỏng hay không
Đây là một đoạn trích từ http. // tài liệu. con trăn. org/2/howto/ổ cắm. html
"Khi recv[] trả về 0 byte, điều đó có nghĩa là phía bên kia đã đóng [hoặc đang trong quá trình đóng] kết nối. Bạn sẽ không nhận được thêm bất kỳ dữ liệu nào trên kết nối này. Bao giờ. Bạn có thể gửi dữ liệu thành công. "
"Một giao thức như HTTP chỉ sử dụng một ổ cắm cho một lần truyền. Máy khách gửi yêu cầu, sau đó đọc phản hồi. Đó là nó. Ổ cắm bị loại bỏ. Điều này có nghĩa là máy khách có thể phát hiện phần cuối của câu trả lời bằng cách nhận 0 byte. "
"Nhưng nếu bạn định sử dụng lại ổ cắm của mình để chuyển tiếp, bạn cần nhận ra rằng không có EOT trên ổ cắm. tôi lặp lại. nếu một ổ cắm gửi hoặc recv[] trả về sau khi xử lý 0 byte, thì kết nối đã bị hỏng. Nếu kết nối không bị ngắt, bạn có thể đợi recv[] mãi mãi, vì ổ cắm sẽ không cho bạn biết rằng không còn gì để đọc [hiện tại]. "
Vì vậy, trong mã, chúng tôi coi kết nối bị tắt khi chúng tôi không thấy thêm dữ liệu nào từ ổ cắm ready_to_read
# process data recieved from client, try: # receiving data from the socket. data = sock.recv[RECV_BUFFER] if data: # there is something in the socket broadcast[server_socket, sock, "\r" + '[' + str[sock.getpeername[]] + '] ' + data] else: # remove the socket that's broken if sock in SOCKET_LIST: SOCKET_LIST.remove[sock] # at this stage, no data means probably the connection has been broken broadcast[server_socket, sock, "Client [%s, %s] is offline\n" % addr]
Đây là mã khách hàng. chat_client. py
# chat_client.py import sys import socket import select def chat_client[]: if[len[sys.argv] < 3] : print 'Usage : python chat_client.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. You can start sending messages' sys.stdout.write['[Me] ']; sys.stdout.flush[] while 1: socket_list = [sys.stdin, s] # Get the list sockets which are readable ready_to_read,ready_to_write,in_error = select.select[socket_list , [], []] for sock in ready_to_read: if sock == s: # incoming message from remote server, s data = sock.recv[4096] if not data : print '\nDisconnected from chat server' sys.exit[] else : #print data sys.stdout.write[data] sys.stdout.write['[Me] ']; sys.stdout.flush[] else : # user entered a message msg = sys.stdin.readline[] s.send[msg] sys.stdout.write['[Me] ']; sys.stdout.flush[] if __name__ == "__main__": sys.exit[chat_client[]]
Mã máy khách sẽ nghe tin nhắn đến từ máy chủ hoặc kiểm tra đầu vào của người dùng. Nếu người dùng nhập một tin nhắn thì hãy gửi nó đến máy chủ
Chúng tôi sử dụng chức năng select[] để xử lý cả hai tin nhắn. một từ stdin là đầu vào của người dùng và một từ máy chủ. Như chúng tôi nhớ lại, việc sử dụng phía máy chủ là chế độ không chặn, chúng tôi không làm gì với hàm select[] và chúng tôi sử dụng nó làm chế độ chặn. Vì vậy, chức năng select[] chặn [chờ] cho đến khi điều gì đó xảy ra. Nó sẽ chỉ trả về khi ổ cắm máy chủ nhận được tin nhắn hoặc người dùng nhập tin nhắn