Python 提供了两个基本的 socket 模块:
- Socket 它提供了标准的BSD Socket API。
- SocketServer 它提供了服务器重心,可以简化网络服务器的开发。
Socket 类型
套接字格式:socket(family, type[,protocal])
使用给定的套接族,套接字类型,协议编号(默认为0)来创建套接字
创建TCP Socket:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
服务器端 Socket 函数
Socket 函数 | 描述 |
s.bind(address) | 将套接字绑定到地址,在AF_INET下,以tuple(host, port)的方式传入,如s.bind((host, port)) |
s.listen(backlog) | 开始监听TCP传入连接,backlog指定在拒绝链接前,操作系统可以挂起的最大连接数,该值最少为1,大部分应用程序设为5就够用了 |
s.accept() | 接受TCP链接并返回(conn, address),其中conn是新的套接字对象,可以用来接收和发送数据,address是链接客户端的地址。 |
"""accept() -> (socket object, address info)
Wait for an incoming connection. Return a new socket
representing the connection, and the address of the client.
For IP sockets, the address info is a pair (hostaddr, port).
"""
fd, addr = self._accept()
客户端 Socket 函数
公共 Socket 函数
Socket 编程思想
TCP 服务器 1、创建套接字,绑定套接字到本地IP与端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind()
2、开始监听链接s.listen()
3、进入循环,不断接受客户端的链接请求
While True:
s.accept()
4、接收客户端传来的数据,并且发送给对方发送数据
s.recv()
s.sendall()
5、传输完毕后,关闭套接字s.close()
TCP 客户端 1、创建套接字并链接至远端地址
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect()
2、链接后发送数据和接收数据
s.sendall()
s.recv()
3、传输完毕后,关闭套接字s.close()
基于TCP的套接字
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
tcp服务端
# 1. 创建服务端的socket对象
serverSocket = socket.socket()
# 2. 绑定一个ip和端口, 客户端连接时的socket;
serverSocket.bind((HOST, PORT))
# 3. 一直监听是否有客户端连接
serverSocket.listen()
inf_loop: #服务器循环链接 无限循环,接收多个链接
# 4. 如果有客户端连接, 接收客户端的连接;
# serverSocket.accept会返回2个值, address指的是, 客户端的ip和端口;
#与客户端的connect相对应建立起来的链接
#clientSocket三次握手建立起来的链接
clientSocket, address = serverSocket.accept()
comm_loop: #通讯循环
# 5. 客户端和服务端进行通信;
data = clientSocket.recv(1024).decode('utf-8')
print("接收到客户端的消息:", data)
# 6. 给客户端回复消息
clientSocket.send(b'hello client')
clientSocket.close() #关闭链接 关闭三次握手即四次挥手
serverSocket.close() #关闭服务器套接字(可选)
tcp客户端
# 1.
clientSocket = socket.socket()
# 2. 连接远程主机
clientSocket.connect((HOST, PORT))
#3
comm_loop: # 通讯循环
# 4. 客户端, 服务端通信
# 给server主机发送消息
clientSocket.send(b'hello server')
# 接收服务端的响应(即服务端回复的消息)
recvData = clientSocket.recv(1024).decode('utf-8')
print("接收到服务器的消息:", recvData)
#5. 关闭客户套接字
clientSocket.close()
Socket 编程实践之服务器端代码
from socket import *
in_port=('127.0.0.1',8080)
back_log=5
buffer_size=1024
tcp_serve=socket(AF_INET,SOCK_STREAM)
tcp_serve.bind(in_port)
tcp_serve.listen(back_log)
print('服务端开始运行了')
conn,addr=tcp_serve.accept() #服务端阻塞
print('双向链接',conn)
print('客户端地址',addr)
while True:
data=conn.recv(buffer_size)
print('来自客户端发送的消息',data.decode('utf-8'))
conn.send(data.upper())
conn.close()
tcp_serve.close()
Socket 编程实践之客户端代码
from socket import *
in_port=('127.0.0.1',8080)
back_log=5
buffer_size=1024
tcp_client=socket(AF_INET,SOCK_STREAM)
tcp_client.connect(in_port)
while True:
msg=input('>>').strip()
if len(msg) == 0:continue #判断是否输入为空 防止两端卡住
tcp_client.send(msg.encode('utf-8'))
print('客户端已发送消息')
data=tcp_client.recv(buffer_size)
print('收到服务端发送到消息',data.decode('utf-8'))
tcp_client.close()
加上链接循环与通信循环
服务端改进版
# import socket
from socket import *
ip_port=('127.0.0.1',8080) #电话卡
back_log=5
buffer_size=1024
tcp_server=socket(AF_INET,SOCK_STREAM) #买手机
tcp_server.bind(ip_port) #手机插卡
tcp_server.listen(back_log) #手机待机
while True: #新增接收链接循环,可以不停的接电话
print('服务端开始运行了')
conn,addr=tcp_server.accept() #服务端阻塞 手机接电话
print('双向链接是',conn)
print('客户端地址',addr)
while True: #新增通信循环,可以不断的通信,收发消息
try: # 防止客户端强制关闭,导致 data=conn.recv(buffer_size)卡住的现象
data=conn.recv(buffer_size) #听消息,听话
print('客户端发来的消息是',data.decode('utf-8'))
conn.send(data.upper()) #发消息,说话
except Exception:
break # 终止
#conn.close() #挂电话
tcp_server.close() #手机关机
客户端改进版
客户端1
#_*_coding:utf-8_*_
import socket
ip_port=('127.0.0.1',8081)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect_ex(ip_port) #拨电话
while True: #新增通信循环,客户端可以不断发收消息
msg=input('>>: ').strip()
if len(msg) == 0:continue #判断是否输入为空 防止两端卡住
s.send(msg.encode('utf-8')) #发消息,说话(只能发送字节类型)
feedback=s.recv(BUFSIZE) #收消息,听话
print(feedback.decode('utf-8'))
s.close() #挂电话
客户端2
# import socket
from socket import *
ip_port=('127.0.0.1',8080)
back_log=5
buffer_size=1024
tcp_client=socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)
while True:
msg=input('>>: ').strip()
if not msg:continue
tcp_client.send(msg.encode('utf-8'))
print('客户端2已经发送消息')
data=tcp_client.recv(buffer_size)
print('收到服务端发来的消息',data.decode('utf-8'))
tcp_client.close()
一个服务端同时可以和两个客户端进行通信
验证客户端的合法性
如果你想在分布式系统中实现一个简单的客户端链接认证功能,又不像SSL那么复杂,那么利用hmac+加盐的方式来实现
#服务端
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *
import hmac,os
secret_key=b'linhaifeng bang bang bang' #加盐
def conn_auth(conn):
'''
认证客户端链接
:param conn:
:return:
'''
print('开始验证新链接的合法性')
msg=os.urandom(32)
conn.sendall(msg)
h=hmac.new(secret_key,msg)
digest=h.digest()
respone=conn.recv(len(digest))
return hmac.compare_digest(respone,digest)
def data_handler(conn,bufsize=1024):
if not conn_auth(conn): #真正通讯之前验证客户端的合法性
print('该链接不合法,关闭')
conn.close()
return
print('链接合法,开始通信')
while True:
data=conn.recv(bufsize)
if not data:break
conn.sendall(data.upper())
def server_handler(ip_port,bufsize,backlog=5):
'''
只处理链接
:param ip_port:
:return:
'''
tcp_socket_server=socket(AF_INET,SOCK_STREAM)
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(backlog)
while True:
conn,addr=tcp_socket_server.accept()
print('新连接[%s:%s]' %(addr[0],addr[1]))
data_handler(conn,bufsize)
if __name__ == '__main__':
ip_port=('127.0.0.1',9999)
bufsize=1024
server_handler(ip_port,bufsize)
#客户端
#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
from socket import *
import hmac,os
secret_key=b'linhaifeng bang bang bang'
def conn_auth(conn):
'''
验证客户端到服务器的链接
:param conn:
:return:
'''
msg=conn.recv(32)
h=hmac.new(secret_key,msg)
digest=h.digest()
conn.sendall(digest)
def client_handler(ip_port,bufsize=1024):
tcp_socket_client=socket(AF_INET,SOCK_STREAM)
tcp_socket_client.connect(ip_port)
conn_auth(tcp_socket_client)
while True:
data=input('>>: ').strip()
if not data:continue
if data == 'quit':break
tcp_socket_client.sendall(data.encode('utf-8'))
respone=tcp_socket_client.recv(bufsize)
print(respone.decode('utf-8'))
tcp_socket_client.close()
if __name__ == '__main__':
ip_port=('127.0.0.1',9999)
bufsize=1024
client_handler(ip_port,bufsize)