1.客户端
打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。
创建Socket时,AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6。SOCK_STREAM指定使用面向流的TCP协议,这样,一个Socket对象就创建成功,但是还没有建立连接。
客户端要主动发起TCP连接,必须知道服务器的IP地址和端口号。端口号由服务器确定。
提供网页服务的服务器必须把端口号固定在80端口,因为80端口是Web服务的标准端口。其他服务都有对应的标准端口号,例如SMTP服务是25端口,FTP服务是21端口,等等。端口号小于1024的是Internet标准服务的端口,端口号大于1024的,可以任意使用。
TCP连接创建的是双向通道,双方都可以同时给对方发数据。但是谁先发谁后发,怎么协调,要根据具体的协议来决定。例如,HTTP协议规定客户端必须先发请求给服务器,服务器收到后才发数据给客户端,此外发送的文本格式必须符合HTTP标准。
接收数据时,调用recv(max)方法,一次最多接收指定的字节数,因此,在一个while循环中反复接收,直到recv()返回空数据,表示接收完毕,退出循环。
# 接收数据: buffer = [] while True: # 每次最多接收1k字节: d = s.recv(1024) if d: buffer.append(d) else: break data = b''.join(buffer)
当我们接收完数据后,调用close()方法关闭Socket
2.服务器
服务器进程首先要绑定一个端口并监听来自其他客户端的连接。
一个Socket依赖4项:服务器地址、服务器端口、客户端地址、客户端端口来唯一确定一个Socket。
服务器需要同时响应多个客户端的请求,所以,每个连接都需要一个新的进程或者新的线程来处理,否则,服务器一次就只能服务一个客户端了。
端口号需要预先指定。因为我们写的这个服务不是标准服务,所以用9999这个端口号。请注意,小于1024的端口号必须要有管理员权限才能绑定 服务器程序通过一个永久循环来接受来自客户端的连接,accept()会等待并返回一个客户端的连接:
3.客户端
import socket # 服务器地址(主机,端口) address = ('127.0.0.1', 9999) # 初始化socket s = socket.socket() # 连接服务器 print('【开始连接服务器 %s:%s】' % address) s.connect(address) while True: print('Server: %s' % s.recv(1024).decode('utf-8')) msg = input('Client: ') if not msg or msg == 'exit': break s.send(msg.encode('utf-8')) # 断开连接 s.close() print('【断开连接】')
4.服务端
import socket import threading import time # 处理客户端连接 def tcp_link(sock, addr): print('【客户端[%s:%s]已连接】' % addr) # 发送连接成功通知 sock.send('【您已成功连接服务器】'.encode('utf-8')) while True: try: # 接收客户端发送的数据 data = sock.recv(1024) time.sleep(1) # 如果接收到空值或者'exit' 则退出 # not data 表示空值: None,Flase,0,[],{},'',b''等 if not data or data.decode('utf-8') == 'exit': break print('Client: %s' % data.decode('utf-8')) sock.send(('Hello, %s' % data.decode('utf-8')).encode('utf-8')) except ConnectionError as e: print(e) break # 断开连接 sock.close() print('【客户端[%s:%s]已断开】' % addr) # 服务器地址(主机,端口) address = ('127.0.0.1', 9999) # 初始化socket s = socket.socket() # 绑定地址 s.bind(address) # 开始监听 s.listen(5) print('【服务器已启动,主机:%s,端口:%s】' % address) print('【等待连接中...】') while True: # 接收客户端socket和地址 sock, addr = s.accept() # 启动子线程处理连接 t = threading.Thread(target=tcp_link, args=(sock, addr)) t.start()