第1章互联网常见架构:
C/S:客户端和服务端
常见:wechat/qq
B/S:浏览器和服务器
常见:所有浏览器都是BS架构
第2章什么是socket?
Socket就是一系列接口,把传输层一下的协议都封装成了简单的接口
2.1为什么要用套接字?
目的是要编写一个CS架构的软件
server端必须具备的特点:
1.一直对外服务
2.必须绑定一个固定的地址
3.支持并发
2.2套接字的类型:
1.基于文件类型的套接字:AF_UNIX
两个文件同时位于一个机器上,则可以共用一个文件系统来进行通信
2.基于网络类型的套接字:AF_INET
2.3套接字的工作流程:
先从服务端说起,服务端先初始化socket,然后与端口绑定,对端口进行监听,调用accept阻塞,等待客户端连接,在这时如果有个客户端初始化一个socket,然后连接服务器connect,如果连接成功,这时客户端与服务端的连接就建立了,客户端发送数据请求,服务端接受请求并处理请求,然后把数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
2.4关于套接字的方法:
服务端套接字函数:
s.bind()绑定(主机,端口号)到套接字
s.listen()开始TCP监听
s.accept()被动接受TCP客户的连接,(阻塞式)等待连接的到来
客户端套接字函数:
s.connect()主动初始化TCP服务器连接
s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
公共用途的套接字函数:
s.recv()接收TCP数据
s.send()发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall()发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom()接收UDP数据
s.sendto()发送UDP数据
s.getpeername()连接到当前套接字的远端的地址
s.getsockname()当前套接字的地址
s.getsockopt()返回指定套接字的参数
s.setsockopt()设置指定套接字的参数
s.close()关闭套接字
面向锁的套接字方法:
s.setblocking()设置套接字的阻塞与非阻塞模式
s.settimeout()设置阻塞套接字操作的超时时间
s.gettimeout()得到阻塞套接字操作的超时时间
2.5基于tcp的socket通信
服务端:
importsocket
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(3)
10
print('来自客户端的请求')
print(addr)
data=conn.recv(1024)
print('来自客户端的消息:',data)
conn.send(data.upper())
conn.close()
客户端:
importsocket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8080))
client.send(bytes('nihao',encoding='utf-8'))
data=client.recv(1024)
print('来自服务端的数据:',data)
client.close()
2.6通信循环问题
服务端:
importsocket
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(3)
conn,addr=server.accept()
print(addr)
while True:
data=conn.recv(1024)
iflen(data) ==0:break
print('来自客户端的消息:',data)
conn.send(data.upper())
conn.close()
客户端:
importsocket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
iflen(msg) ==0:continue
client.send(bytes(msg,encoding='utf-8'))
data=client.recv(1024)
print(data)
client.close()
2.7循环链接问题
importsocket
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,addr=server.accept()
while True:
try:
data=conn.recv(1024)
iflen(data) ==0:break
print(data)
conn.send(data.upper())
exceptConnectionRefusedErrorase:
break
conn.close()
2.8tcp版ssh客户端
服务端:
importsocket
importsubprocess
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,addr=server.accept()
while True:
try:
data=conn.recv(1024)
iflen(data) ==0:break
obj=subprocess.Popen(data.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
conn.send(stdout+stderr)
exceptConnectionRefusedErrorase:
break
conn.close()
server.close()
客户端:
importsocket
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
iflen(msg) ==0:continue
client.send(bytes(msg,encoding='utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
client.close()
第3章粘包问题
要知道:只有tcp有粘包现象,UDP则永远没有
3.1什么是粘包?
就是接受方不知道消息之间的界限,不知道一次性提取多少字节所造成的
3.2解决办法:
问题的根源在于,接受端不知大发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接受所有数据即可
解决粘包问题服务端:
importsocket
importstruct
importsubprocess
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
conn,addr=server.accept()
while True:
try:
data=conn.recv(1024)
iflen(data) ==0:break
obj=subprocess.Popen(data.decode('utf-8'),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE
)
stdout=obj.stdout.read()
stderr=obj.stderr.read()
#发送固定长度的报头
total_size=len(stdout) +len(stderr)
conn.send(struct.pack('i',total_size))
#真实数据
conn.send(stdout+stderr)
exceptConnectionRefusedErrorase:
break
conn.close()
server.close()
客户端:
importsocket
importstruct
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('>>: ').strip()
iflen(msg) ==0:continue
client.send(bytes(msg,encoding='utf-8'))
#接受数据长度
header=client.recv(4)
total_size=struct.unpack('i',header)[0]
recv_size=0
res=b''
whilerecv_size < total_size:
recv_data=client.recv(1024)
res+=recv_data
recv_size+=len(recv_data)
print(res.decode('utf-8'))
client.close()
第4章一个简单的基于UDP的socket客户端和服务端
服务端:
importsocket
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080))
while True:
data,client_addr=server.recvfrom(1024)
print('===>',data,client_addr)
server.sendto(data.upper(),client_addr)
server.close()
客户端:
importsocket
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
msg=input('>>: ').strip()
client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
data,server_addr=client.recvfrom(1024)
print(data)
client.close()