1、基础知识
1.1 网络编程(CS/BS架构)
1.1.1 c/s 架构
C/S即:Client与Server ,中文意思:客户端与服务器端架构,这种架构也是从用户层面(也可以是物理层面)来划分的。
这里的客户端一般泛指客户端应用程序EXE,程序需要先安装后,才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大
1.1.2 b/s 架构
B/S即:Browser与Server,中文意思:浏览器端与服务器端架构,这种架构是从用户层面来划分的。
Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。
1.1.3 tcps三次握手/四次断开
本文不做介绍,此项是重点
1.1.4 socket
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。
1.1.5 tcp/udp
TCP(Transmission Control Protocol)可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。
UDP(User Datagram Protocol)不可靠的、无连接的服务,传输效率高(发送前时延小),一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。
2、 基于TCP 的socket
注: tcp 是基于链接的 必须先启动服务端,然后在启动客户端去链接服务端
##server###
import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8888)) #把地址绑定到套接字
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',8888)) # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024) # 对话(发送/接收)
print(ret)
sk.close() # 关闭客户套接字
以上的传输是固定字段的传输我们可以改为input 方式进行传输
3、基于udp
#server:
import socket
sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字
sk.bind(('127.0.0.1',9000)) #绑定服务器套接字
connect,addr = sk.recvfrom(1024)
print(connect)
sk.sendto = (b'he',addr)
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)
###实现QQ聊天
#server
import socket
ip_port=('127.0.0.1',8081)
server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server_sock.bind(ip_port)
while True:
msg,addr=server_sock.recvfrom(1024)
print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],msg.decode('utf-8')))
back_msg=input('回复消息: ').strip()
server_sock.sendto(back_msg.encode('utf-8'),addr)
#client
import socket
BUFSIZE=1024
client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
qq_name_dic={
'测试1':('127.0.0.1',8081),
'测试2':('127.0.0.1',8081),
'测试3':('127.0.0.1',8081),
'测试4':('127.0.0.1',8081),
}
while True:
name=input('请选择聊天对象: ').strip()
while True:
msg=input('请输入消息,回车发送,输入q结束和他的聊天: ').strip()
if msg == 'q':break
if not msg or not name or name not in qq_name_dic:continue
client_socket.sendto(msg.encode('utf-8'),name_dic[name])
back_msg,addr=client_socket.recvfrom(BUFSIZE)
print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
client_socket.close()
4、黏包问题
4.1 何为黏包
黏包就是当你发送
ceshi
ceshi
结果对方收到的是ceshiceshi 两行合并成一行了即为黏包,问题出现的原因是: 程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗
解决黏包问题的思路:
我们可以借助一个模块,这个模块可以把要发送的数据长度转换成固定长度的字节。
这样客户端每次接收消息之前只要先接受这个固定长度字节的内容看一看接下来要接收的信息大小,
那么最终接受的数据只要达到这个值就停止,就能刚好不多不少的接收完整的数据了
4.2 实例:
###此方式解决黏包问题的思路就是 接收4个字节,根据这4个字节获取报头,从报头过去filesize, 然后根据filesize 接收文件
##server
import socket
import json
import os
server=socket.socket()
server.bind(("192.168.59.1",8600))
server.listen(5)
conn,addr=server.accept()
ret_str=conn.recv(1024).decode("utf-8")
ret=json.loads(ret_str)
file_name=ret.get("filename")
action=ret.get("action")
file_size=ret.get("file_size")
conn.sendall(b"200")
with open(file_name,"wb") as f1:
conn_len = 0
while file_size>conn_len:
msg=conn.recv(1024)
conn_len+=len(msg)
f1.write(msg)
print(f'文件大写{file_size}---文件传输大小{conn_len}')
print("失败")
##cilent
client1 不黏包 多发送接收次recv' 方法一
import socket
import json
import os
client=socket.socket()
client.connect(("192.168.59.1",8600))
while True:
cmd=input("请输入命令:")
action,filename=cmd.strip().split(" ")
flie_size=os.path.getsize(filename)
ret={"action":action,"filename":filename,"file_size":flie_size}
ret_str=json.dumps(ret).encode("utf-8")
client.sendall(ret_str)
code=client.recv(1024).decode("utf-8")
if code=="200":
with open(filename,"rb")as f1:
for line in f1:
client.sendall(line)
else:
print("失败")
5、 socketserver 模块
#应用场景
应为tcp是一个长连接 只能保持一个人通话 但是socketserver就解决了同时多个客户端来 通话
#server
import socketserver
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
conn =self.request
while True:
conn.send(b'hellp')
server = socketserver.ThreadingTCPServer(('127.0.0.1',9001),Myserver)
server.serve_forever()
#client
client1
import socket
clinet=socket.socket()
clinet.connect(("192.168.59.1",9001))
name=input("我是客户:")
clinet.send(name.encode("utf-8"))
ret=clinet.recv(1024).decode("utf-8")
print(ret)