介绍:
- 面向Socket编程进阶(具体客户端,服务端语法)
- 使用线程使得服务端服务多个用户
- 用户之间的通信
面向Socket编程进阶
服务端
创建Socket服务端的步骤
- 创建服务端端套接字对象 创建服务端socket对象 ==>so = socket.socket(AddressFamily, Type)
- 绑定端口号 bind((host, port)) 表示绑定端口号, host 是 ip 地址,port 是端口号
- 设置监听 listen (backlog) 表示设置监听,backlog参数表示最大等待建立连接的个数(128)
- 等待接受客户端的连接请求 accept() 表示等待接受客户端的连接请求
- 接收数据 recv(buffersize) 表示接收数据, buffersize 是每次接收数据的长度
- 发送数据 send(data) 表示发送数据,data 是二进制数据
- 关闭套接字 so.close()
服务端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import socket
if __name__ == "__main__": server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind(('0.0.0.0',8000)) while True: server.listen(128) service_socket, ip_port = server.accept() print("客户端的端口号:",ip_port) recv_data = service_socket.recv(1024) recv_data_length = len(recv_data) recv_content = recv_data.decode("gbk") print("客户端发来的数据是:",recv_content) info = "我知道了小威".encode("gbk") service_socket.send(info)
|
客户端
创建客户端步骤:
- 创建客户端套接字对象 so = socket.socket(AddressFamily, Type)
- 和服务端套接字建立连接 so.connect((host, port))
- 发送数据 so.send(data)
- 接收数据 recv(buffersize) 表示接收数据, buffersize是每次接收数据的长度
- 关闭客户端套接字 so.close()
客户端代码:
1 2 3 4 5 6 7 8 9 10 11 12
| import socket
if __name__ == '__main__': client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('10.211.55.32',8000)) print("连接成功!") data = "你好,我是cilent小威".encode("gbk") client.send(data) accept_data = client.recv(1024) recv_data = accept_data.decode("gbk") print("服务器返回的数据是:",recv_data)
|
多任务版TCP服务端程序开发
具体实现步骤
- 编写一个TCP服务端程序,循环等待接受客户端的连接请求
- 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞
- 把创建的子线程设置成为守护主线程,防止主线程无法退出。
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
| import threading import socket
def handle_client_request(service_sock,ip_port): while True: data = service_sock.recv(1024) if data: print(data.decode("gbk"),ip_port) service_sock.send("好的已收到!".encode("gbk")) else: print("客户端下线了!",ip_port) break
if __name__ == '__main__': server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) server.bind(('0.0.0.0', 8000)) server.listen(128) while True: service_sock, ip_port = server.accept() handle_thread = threading.Thread(target=handle_client_request,args=(service_sock,ip_port)) handle_thread.setDaemon(True) handle_thread.start()
|
用户间通信
如何实现用户之间的通信?用户之间能直接通信吗?
很久之前我就是以为用户之间能直接通信,但是学了面向Socket编程,才发现用户之间是不能直接通信的,要通过服务器的转发来间接实现用户之间的通信。
下面的代码是我很久之前写的代码,过了两年忘的一干二净。哈哈!由此,我愈来愈感受到写注释的重要,以及时时刻刻做好记录是多么的重要。
QQ_Server(服务端)
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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import socket import json import threading from collections import defaultdict
online_users = defaultdict(dict)
user_msgs = defaultdict(list)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('0.0.0.0', 8000)) server.listen(128)
def handle_client_request(sock, addr): while True: recv_data = sock.recv(1024) json_data = json.loads(recv_data.decode("utf8")) action = json_data.get("action", "") if action == 'login': online_users[json_data["user"]] = sock sock.send("登陆成功".encode("utf8")) elif action == 'list_user': all_users = [user for user, sock in online_users.items()] sock.send(json.dumps(all_users).encode("utf8")) elif action == 'history_msg': sock.send(json.dumps(user_msgs.get(json_data["user"])).encode("utf8")) elif action == 'send_msg': if json_data["to"] in online_users: online_users[json_data["to"]].send(json.dumps(json_data).encode("utf8")) user_msgs[json_data["to"]].append(json_data) elif action == 'exit': del online_users[json_data["user"]] sock.send("退出成功".encode("utf8"))
if __name__ == '__main__': while True: sock, addr = server.accept() client_thread = threading.Thread(target=handle_client_request,args=(sock,addr)) client_thread.start()
|
QQ_Client(客户端)
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 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 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
| import socket import json import threading
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(('10.211.55.32', 8000))
user = 'Penguin'
login_templete = { "action": "login", "user": user }
client.send(json.dumps(login_templete).encode('utf8')) res = client.recv(1024) print(res.decode('utf8'))
get_user_templete = { "action": "list_user" }
client.send(json.dumps(get_user_templete).encode('utf8')) res = client.recv(1024) print("当前在线用户:{}".format(res.decode("utf8")))
offonline_msg_templete = { "action": "history_msg", "user": user } client.send(json.dumps(offonline_msg_templete).encode("utf8")) res = client.recv(1024) print("历史消息{}".format(res.decode("utf8")))
exit = False
def handle_receive(): while True: if not exit: try: res = client.recv(1024) except: break try: res_json = json.loads(res) msg = res_json["data"] from_user = res_json["from"] print("收到来自用户({})的信息{}:".format(from_user,msg)) except: print(res.decode("utf8")) else: break
def handle_send(): while True: op_type = input("请输入您要进行的操作:1,发送消息,2,退出,3,获取当前在线用户") if op_type not in ["1", "2", "3"]: print("不支持该操作") op_type = input("请输入您要进行的操作:1,发送消息,2,退出,3,获取当前在线用户") elif op_type == '1': to_user = input("请输入您要发送到的用户:") msg = input("请输入您要发送的信息:") send_data_templete = { "action": "send_msg", "to": to_user, "from": user, "data": msg } client.send(json.dumps(send_data_templete).encode("utf8")) elif op_type == '2': exit_templete = { "action": "exit", "user": user } client.send(json.dumps(exit_templete).encode("utf8")) exit = True client.close() elif op_type == '3': get_user_templete = { "action": "list_user" } client.send(json.dumps(get_user_templete).encode("utf8"))
if __name__ == '__main__': send_thread = threading.Thread(target=handle_send) send_thread.start() recv_thread = threading.Thread(target=handle_receive) recv_thread.start()
|
搭建静态Web服务器
访问固定页面
实现步骤:
- 编写一个TCP服务端程序
- 获取浏览器发送的http请求报文数据
- 读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器。
- HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字。
浏览器在地址页面输入:http://localhost:8000/即可访问web页面
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 36 37
| import socket
if __name__ == '__main__': server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,True) server.bind(('0.0.0.0', 8000)) server.listen(128) while True: sock, addr = server.accept() recv_data = sock.recv(4096) data = recv_data.decode("utf8") print("客户端发送的数据是:{}".format(data))
with open('2/page01.html','rb') as f: f_data = f.read()
response_line = "HTTP/1.1 200 ok \r\n" response_header = "Server: PWS1.0\r\n" response_body = f_data response_data = (response_line + response_header + "\r\n").encode("utf8") + response_body
sock.send(response_data)
sock.close()
|
访问指定页面
返回指定页面数据的实现步骤:
- 获取用户请求资源的路径
- 根据请求资源的路径,读取指定文件的数据
- 组装指定文件数据的响应报文,发送给浏览器
- 判断请求的文件在服务端不存在,组装404状态的响应报文,发送给浏览器
注意:
- 被返回的HTML文件被当做请求体的时候不需要解码,即encode(“utf8”)
- 响应行”HTTP/1.1 200 ok\r\n”和响应头”Server: PWS1.0\r\n”要写对
- try,except,else,finally中else 是没有异常才执行,finally有没有异常都要执行
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 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 70 71 72 73
| import socket
def main(): server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) server.bind(('0.0.0.0', 8000)) server.listen(128) while True: sock, addr = server.accept() recv_data = sock.recv(4096) if len(recv_data) == 0: print("关闭浏览器了") sock.close() return
recv_data_content = recv_data.decode("utf8") print(recv_data_content) request_list = recv_data_content.split(" ", maxsplit=2)
request_path = request_list[1] print("客户端请求的页面是:",request_path)
if request_path == "/": request_path = "/index.html"
try: with open("static" + request_path, 'rb') as f: file_data = f.read() except Exception as e: response_line = "HTTP/1.1 404 Not Found\r\n" response_header = "Server: PWS1.0\r\n" with open("static/error.html", "rb") as f: file_data = f.read() response_body = file_data response_data = (response_line + response_header + "\r\n").encode("utf8") + response_body sock.send(response_data) else: response_line = "HTTP/1.1 200 ok\r\n" response_header = "Server: PWS1.0\r\n" response_body = file_data response_data = (response_line + response_header + "\r\n").encode("utf8") + response_body sock.send(response_data) finally: sock.close()
if __name__ == '__main__': main()
|
多线程服务器
目前的Web服务器,不能支持多用户同时访问,只能一个一个的处理客户端的请求,那么如何开发多任务版的web服务器同时处理多个客户端的请求?
可以使用多线程,比进程更加节省内存资源。
多任务版web服务器程序的实现步骤:
- 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞。
- 把创建的子线程设置成为守护主线程,防止主线程无法退出。
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 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 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| import socket import threading
def handle_client_request(sock, addr): recv_data = sock.recv(4096) if len(recv_data) == 0: print("用户关闭了浏览器!") sock.close() return
recv_data_content = recv_data.decode("utf8") request_list = recv_data_content.split(" ", maxsplit=2)
request_path = request_list[1] print("客户端请求的页面是:", request_path)
if request_path == "/": request_path = "static/index.html" try: with open(request_path, "rb") as f: file_data = f.read() except Exception as e: with open("static/error.html", "rb") as f: file_data = f.read() response_line = "HTTP/1.1 404 Not Found\r\n" response_header = "Server: PWS1.0\r\n" response_body = file_data response_data = (response_line + response_header).encode("utf8") + response_body sock.send(response_data) else: response_line = "HTTP/1.1 200 ok\r\n" response_header = "Server: PWS1.0\r\n" response_body = file_data response_data = (response_line + response_header + "\r\n").encode("utf8") + response_body sock.send(response_data) finally: sock.close()
def main(): server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) server.bind(('0.0.0.0', 8000)) server.listen(128) while True: sock, addr = server.accept() handle_res = threading.Thread(target=handle_client_request, args=(sock, addr)) handle_res.setDaemon(True) handle_res.start()
if __name__ == '__main__': main()
|