socket(套接字)
基于tcp协议的socket
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
server端
import socket sk = socket.socket() sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字 sk.listen() #监听链接 conn,addr = sk.accept() #接受客户端链接 ret = conn.recv(1024) #接收客户端信息 print(ret) #打印客户端信息 conn.send(b'hi') #向客户端发送信息 conn.close() #关闭客户端套接字 sk.close() #关闭服务器套接字(可选)
client端
import socket sk = socket.socket() # 创建客户套接字 sk.connect(('127.0.0.1',8898)) # 尝试连接服务器 sk.send(b'hello!') ret = sk.recv(1024) # 对话(发送/接收) print(ret) sk.close() # 关闭客户套接字
问题:有时重启服务端可能会遇到
解决办法:
#加入一条socket配置,重用ip和端口 import socket from socket import SOL_SOCKET,SO_REUSEADDR sk = socket.socket() sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字 sk.listen() #监听链接 conn,addr = sk.accept() #接受客户端链接 ret = conn.recv(1024) #接收客户端信息 print(ret) #打印客户端信息 conn.send(b'hi') #向客户端发送信息 conn.close() #关闭客户端套接字 sk.close() #关闭服务器套接字(可选)
完成一个socket实现的小程序:
- 能够实现和同桌之间的通信
- 能够实现自己向发送的任意内容
- 能够和你的同桌聊任意多句话,并设置退出
server端
import socket sk = socket.socket() # 创建一个对象 sk.bind(('127.0.0.1',9001)) # 绑定一个服务器的地址 192.168.16.46 sk.listen() # 开始接受客户端给我连接 conn,addr = sk.accept() # 阻塞 直到有人连我 while True: conn.send(b'hello') # 发送内容 msg = conn.recv(1024) # 收信 if msg.decode('utf-8') == 'tuichu': conn.send("tuichu".encode("utf-8")) break conn.close() # 关闭连接 sk.close() # 关闭服务器 print(msg.decode("utf-8"))
client端
import socket sk = socket.socket() # 创建socket对象 sk.connect(('127.0.0.1',9001)) # 绑定连接server端的地址 while True: msg = sk.recv(1024) # 接收服务器发来的信息 print(msg.decode('utf-8')) # 解码并打印消息内容 if msg.decode('utf-8') == 'tuichu': break sk.close() # 关机 choice = input('请输入您要发送的内容>>>:') sk.send(choice.encode('utf-8'))
基于udp协议的socket
udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接
server端
import socket udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字 udp_sk.bind(('127.0.0.1',9000)) #绑定服务器套接字 msg,addr = udp_sk.recvfrom(1024) print(msg) udp_sk.sendto(b'hi',addr) # 对话(接收与发送) udp_sk.close() # 关闭服务器套接字
client端
import socket ip_port=('127.0.0.1',9000) udp_sk=socket.socket(type=socket.SOCK_DGRAM) udp_sk.sendto(b'hello',ip_port) back_msg,addr=udp_sk.recvfrom(1024) print(back_msg.decode('utf-8'),addr)
socket更多方法介绍
服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect() 主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数
s.recv() 接收TCP数据
s.send() 发送TCP数据
s.sendall() 发送TCP数据
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getpeername() 连接到当前套接字的远端的地址
s.getsockname() 当前套接字的地址
s.getsockopt() 返回指定套接字的参数
s.setsockopt() 设置指定套接字的参数
s.close() 关闭套接字
面向锁的套接字方法
s.setblocking() 设置套接字的阻塞与非阻塞模式
s.settimeout() 设置阻塞套接字操作的超时时间
s.gettimeout() 得到阻塞套接字操作的超时时间
面向文件的套接字的函数
s.fileno() 套接字的文件描述符
s.makefile() 创建一个与该套接字相关的文件
tcp验证登录
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket import hashlib import json def md5_func(user,pwd): md5 = hashlib.md5(user.encode('utf-8')) md5.update(pwd.encode('utf-8')) return md5.hexdigest() username = input('用户名') password = input('密 码') sk = socket.socket() sk.connect(('127.0.0.1',9000)) res = {'opt':'login','username':username,'password':md5_func(username,password)} ret = json.dumps(res) sk.send(ret.encode('utf-8')) msg = sk.recv(1024).decode('utf-8') # {"opt": "login", "result": false} a = json.loads(msg) if a['result']: print('登陆成功') else: print('登陆失败') sk.close()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: utf-8 -*- # @Time : 2019/4/12 15:33 import os import json import hmac import socket import struct import hashlib sk = socket.socket() # 登录密码加密 def get_md5(user,pwd): md5 = hashlib.md5(user.encode('utf-8')) md5.update(pwd.encode('utf-8')) return md5.hexdigest() # 认证加密 def get_hmac(secret_key,rand): hmac_t = hmac.new(secret_key, rand) res = hmac_t.digest() return res # 发送数据 def pro_send(sk,dic,pro = True): bytes_dic = json.dumps(dic).encode('utf-8') if pro: len_bytes = struct.pack('i',len(bytes_dic)) sk.send(len_bytes) sk.send(bytes_dic) # 接收数据 def pro_recv(sk,pro=True,num=1024): if pro: num = sk.recv(4) num = struct.unpack('i', num)[0] str_dic = sk.recv(num).decode('utf-8') dic = json.loads(str_dic) return dic # 客户端认证 def auth(): sk.connect(('127.0.0.1', 9000)) secret_key = '宋治学'.encode('utf-8') rand = sk.recv(1024) res = get_hmac(secret_key,rand) sk.send(res) return sk # 将对象return出来方便全局使用 # 下载文件 def write_file(filename): md5 = hashlib.md5() filesize = json.loads(sk.recv(1024).decode('utf-8')) with open(filename, 'wb') as f: print('正在传输') while filesize['file_size'] > 0: content = sk.recv(4096) md5.update(content) f.write(content) filesize['file_size'] -= len(content) print('传输完成') return md5.hexdigest() # D:\PycharmProjects\s20\爬虫学习\server.py def download(sk): dic = {'operate': 'download'} pro_send(sk, dic) lis = pro_recv(sk, pro=False, num=1024) for index,i in enumerate(lis,1): print(index,i) choice = input('请选择要下载的文件').strip() if lis[int(choice)-1]: sk.send(lis[int(choice)-1].encode('utf-8')) # 发送用户选择的文件名 md5_file = write_file(lis[int(choice) - 1]) # 下载文件后得到的md5值 ret_md5 = sk.recv(1024).decode('utf-8') # 服务器传过来的md5值 if md5_file == ret_md5: print('文件校验成功') else: print('文件校验失败') else: print('输入有误请重新输入') # 上传文件 def upload(sk): file_path = input('请输入文件路径').strip() file_name = os.path.basename(file_path) file_size = os.path.getsize(file_path) dic = {'filename': file_name, 'filesize': file_size, 'operate': 'upload'} pro_send(sk, dic) with open(file_path,'rb') as f: while file_size > 4096: content = f.read(4096) sk.send(content) file_size -= len(content) else: content = f.read() sk.send(content) # 退出 def exsit(sk=None): exit() # 登录 def login(user,pwd): sk = auth() dic = {'user': user, 'passwd': get_md5(user,pwd), 'operate': 'login'} pro_send(sk,dic) ret = pro_recv(sk) return sk,ret # 用户登录成功选择操作 def choice(sk): while True: operate = [('上传', upload), ('下载', download),('退出',exsit)] for index,opt in enumerate(operate,1): print(index,opt[0]) num = input('请选择操作:>>>').strip() if num.isdigit() and int(num) in range(1,len(operate)+1): operate[int(num)-1][1](sk) else:print('输入有误请重新输入') while True: username = input('username:').strip() password = input('password:').strip() sk,ret = login(username,password) if ret['flag']: print('登录成功') choice(sk) else: print('登陆失败') sk.close() break
udp多人聊天
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# -*- coding: utf-8 -*- # @Time : 2019/4/9 16:35 import socket server_socket = socket.socket(type=socket.SOCK_DGRAM) server_socket.bind(('127.0.0.1',9000)) while True: conn,addr = server_socket.recvfrom(1024) print('消息来自%s:%s %s'%(addr[0],addr[1],conn.decode('utf-8'))) msg = input('回复消息>>>:') server_socket.sendto(msg.encode('utf-8'),addr)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket client_socket = socket.socket(type=socket.SOCK_DGRAM) dic = { '华哥':('127.0.0.1',9000), '黑哥':('127.0.0.1',9000), '李健':('127.0.0.1',9000), '钱羽':('127.0.0.1',9000), '雄哥':('127.0.0.1',9000), '王月阳':('127.0.0.1',9000), } while True: name = input('请选择聊天对象').strip() name = dic[name] while True: msg = input('>>>:').strip() client_socket.sendto(msg.encode('utf-8'),name) a,b = client_socket.recvfrom(1024) print(a.decode('utf-8'))