一.Socket层
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.
把复杂的TCP/IP协议族隐藏在Socket接口后面.让Socket去组织数据,以符合指定的协议
可以理解socket就是一个模块,也可理解成ip+port
只要确立了ip和port就能找到一个应用程序,并且使用socket模块来与之通信
2.套接字(socket):
一开始,套接字被设计用在同一台主机上多个应用程序之间的通讯
套接字有两种:
基于文件型:套接字家族的名字:AF_UNIX
基于网络型:套接字家族的名字:AF_INET
3.socket参数
socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)
创建socket对象的参数说明:
SOCK_STREAM 是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送。
SOCK_DGRAM 是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播信息。
proto 协议号通常为零,可以省略。
fileno 如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。
4.服务端套接字函数
s.bind() 绑定(主机,端口号)到套接字
s.listen() 开始TCP监听
s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
客户端套接字函数:s.connect() 主动初始化TCP服务器连接
5.公共用途的套接字函数:
s.recv() 接收TCP数据
s.send() 发送TCP数据(待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall()发送完整的TCP数据(待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom() 接收UDP数据
s.sendto() 发送UDP数据
s.getsockname() 当前套接字的地址
s.close() 关闭套接字
####阻塞####
阻塞:如果阻塞模式则等待数据
非阻塞:如果非阻塞模式有数据返回数据、无数据直接返回报错
UDP中服务端有几个recvfrom就要对应几个sendinto
sk.setblocking(bool)
是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错
二.基于TCP协议的socket
tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
简单通信:server端
import socket sk=socket.socket() #创建了一个socket对象 #sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) 如果端口冲突加上这个 sk.bind(('127.0.0.1',9000)) #绑定一台机器的(ip,端口) sk.listen() ##建立监听等待别人连接 corn,add=sk.accept() #阻塞:在这里等待直到接到一个连接 # conn是连接 # addr是对方的地址 corn.send(b'hello') #和对方打招呼 msg=corn.recv(1024) #接收对方的信息 print(msg) corn.close() #关掉连接 sk.close()
client端
import socket sk=socket.socket() sk.connect(("127.0.0.1",9000)) ret=sk.recv(1024) print(ret) sk.send(b'HELLO') sk.close()
简单的聊天例子:server端
import socket sk=socket.socket() sk.bind(('127.0.0.1',9000)) sk.listen() corn,add=sk.accept() while True: inp=input("《《") if inp == "q": corn.send(inp.encode("utf-8")) break corn.send(inp.encode("utf-8")) msg=corn.recv(1024) if msg.decode("utf-8") =="q":break print(msg.decode("utf-8")) corn.close() sk.close()
client端
import socket sk=socket.socket() sk.connect(("127.0.0.1",9000)) while True: ret=sk.recv(1024) if ret.decode("utf-8") == "q":break print(ret.decode("utf-8")) sed=input("<<") if sed.encode(("utf-8")) == "q": sk.send(sed.encode(("utf-8"))) break sk.send(sed.encode(("utf-8"))) sk.close()
三.基于UDP协议的socket
udp是无链接的,先启动哪一端都不会报错
简单通信:server端
import socket sk=socket.socket(type=socket.SOCK_DGRAM) # 建立一个socket对象,指定以UDP协议的形式来连接 sk.bind(("127.0.0.1",9000)) # 指定服务的地址 msg,add=sk.recvfrom(1024) # 接收消息,发送端的地址 print(msg) sk.sendto(b'hello',add) #给发送端回复消息 sk.close() #关闭socket连接
client端
import socket sk=socket.socket(type=socket.SOCK_DGRAM) sk.sendto(b"HELLO",("127.0.0.1",9000)) ##直接给服务器发送一段消息 msg,add=sk.recvfrom(1024) # #接收对面的回信 print(msg) sk.close()
简单的聊天例子:server端
import socket sk=socket.socket(type=socket.SOCK_DGRAM) sk.bind(("127.0.0.1",9000)) while True: msg,add=sk.recvfrom(1024) print("来自%s的一条消息:%s"%(add,msg.decode("utf-8"))) message=input("》》") sk.sendto(message.encode("utf-8"),add) sk.close()
client端
import socket sk=socket.socket(type=socket.SOCK_DGRAM) while True: message = input("》》") add=("127.0.0.1",9000) sk.sendto(message.encode("utf-8"), add) msg,add=sk.recvfrom(1024) print(msg.decode("utf-8")) sk.close()
时间服务器:客户端每隔一段时间发送请求到服务端,发送时间的格式
简单通信:server端
import time import socket sk=socket.socket(type=socket.SOCK_DGRAM) sk.bind(("127.0.0.1",9000)) while True: msg,add=sk.recvfrom(1024) msg=time.strftime(msg.decode("utf-8")) sk.sendto( msg.encode("utf-8"),add) sk.close()
client端
import time import socket sk=socket.socket(type=socket.SOCK_DGRAM) while True: add=("127.0.0.1",9000) msg_time="%Y-%m-%d %H:%M:%S" sk.sendto(msg_time.encode("utf-8"),add) msg,add=sk.recvfrom(1024) print(msg.decode("utf-8")) time.sleep(1) sk.close()