1.socket概念
也叫做套接字。用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求,说白了就是一个接口
2.用法
first:实例化对象,构造函数:
obj = socket(family, type, proto, fileno, fileno)
family:地址簇,表示使用的协议,即TCP/IPv4的协议
type:表示TCP或者UDP
protocol :协议号,默认为0,一般不填
fileno:如果指定了fileno,其他参数将被忽略
second:进行绑定:
socket.bind(address)
address:IP地址与端口,这里是一个元组
third:开始监听:
socket.listen()
之前版本listen要添加参数n,参数n表示同一时间可以有n个链接等待与server端通信,一般不用
fourth:调用套接字函数等待连接
connection,address = socket.accept()
调用accept方法时,socket会进入'waiting'(或阻塞)状态。客户请求连接时,方法建立连接并返回服务器。accept方法返回一个含有俩个元素的元组,形如(connection,address)。第一个元素(connection)是新的socket对象,服务器通过它与客户通信;第二个元素(address)是客户的internet地址。
fifth:处理数据
服务器和客户通过send和recv方法传输数据。
服务器调用send,采用字符串形式向客户发送信息,send方法返回已发送的字符个数。
服务器使用recv方法从客户接受信息。
调用recv时,必须指定一个整数来控制本次调用所接受的最大数据量。recv方法在接受数据时会进入'blocket'状态,最后返回一个字符串,用它来表示收到的数据。如果发送的量超过recv所允许,数据会被截断。多余的数据将缓冲于接收端。以后调用recv时,多余的数据会从缓冲区删除。
sixth:关闭套接字
用法案例:
基于TCP协议的socket(TCP是基于链接的,必须先启动服务端,自启动客户端去链接客户端)
server端:
import socket soc = socket.socket() # 创建一个soc对象 soc.bind(('127.0.0.1',9789)) # 将地址绑定到套接字 soc.listen() # 监听端口 conn,addr = soc.accept() # 接收客户端口链接 re = conn.recv(1024) # 接收客户端信息 print(re.decode('utf-8')) # 接收客户端发送的信息,必须转码成unicode conn.send('海贼王'.encode('utf-8')) # 给客户端发送信息,必须是bytes类型 conn.close() soc.close()
client端:
import socket me = socket.socket() # 实例化对象 me.connect(('127.0.0.1',9789)) # 把地址绑定到套接字 me.send('one piece'.encode('utf-8')) # 给server发送消息 ret = me.recv(1024) # 设置固定接收字节,防止一次加载过多占内存 print(ret.decode('utf-8')) # 将server端发来的信息解码 me.close() # 关闭端口
易错点:这里客户端和服务端发送消息时,必须注意接收与发送顺序
计算机回环地址:
127.0.0.1,默认为本机地址,一般在自己电脑测试使用,它不用再过交换机查询
tcp协议适用范围:
适用于文件的上传和下载,以及发送重要文件等。每和一个客户端建立连接,都会在自己的操作系统上占用一个资源
它同一个时间段只能和一个客户端建立连接
基于UDP协议的socket(UDP是无链接的,先启动哪一端都不会报错)
用法案例:
server端:
import socket ser = socket.socket(type=socket.SOCK_DGRAM) ser.bind(('127.0.0.1', 8888)) mes,addr = ser.recvfrom(1024) print(mes.decode('utf-8')) ser.sendto('海贼王'.encode('utf-8'), addr) ser.close()
client端:
import socket client = socket.socket(type=socket.SOCK_DGRAM) addr = ('127.0.0.1', 8888) client.sendto('onepiece'.encode('utf-8'),addr) mess,addr = client.recvfrom(1024) print(mess.decode('utf-8')) client.close()
这样写虽然没有什么问题,但是基于需要来回编码以及解码,显得很不pythonic,为了解决这个问题,我们特别为内置的socket类编写一个子类,来解决编码转换的问题
创建类方法:
from socket import * class Mysocket(socket): def __init__(self,coding='utf-8'): self.coding = coding super().__init__(type=SOCK_DGRAM) def mysend(self,mess,addr): return self.sendto(mess.encode(self.coding),addr) def myrecv(self,num): mess,addr = self.recvfrom(num) return mess.decode(self.coding),addr
创建客户端和接收端
# server端 from my_test import Mysocket # 从文件路径中导入这个类 ser = Mysocket() ser.bind(('127.0.0.1', 8888)) mes,addr = ser.myrecv(1024) print(mes) ser.mysend('海贼王', addr) ser.close() # 用户端 from my_test import Mysocket client = Mysocket() addr = ('127.0.0.1', 8888) client.mysend('onepiece',addr) mess,addr = client.myrecv(1024) print(mess) client.close()
在UDP下基于服务端完成客户端的时间同步服务,比如机房中的所有机器每隔一段时间都会请求服务器,来获取一个标准时间
# server端
import time import socket sk = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',9200) sk.bind(ip_port) while True: msg,addr = sk.recvfrom(1024) # 接收的是用户端格式化字符串字节码 sk.sendto(time.strftime(msg.decode('utf-8')).encode('utf-8'),addr) sk.close()
# client端
import socket import time tb = socket.socket(type=socket.SOCK_DGRAM) ip_port = ('127.0.0.1',9200) while True: tb.sendto('%Y/%m/%d %H:%M:%S'.encode('utf-8'),ip_port) mes,addr = tb.recvfrom(1024) print(mes.decode('utf-8')) time.sleep(1) tb.close()