初学Python——Socket网络编程
认识socket
socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递。我们知道网络 通信 都 是基于 ip+port(端口) 方能定位到目标的具体机器上的具体服务,操作系统有0-65535个端口,每个端口都可以独立对外提供服务,如果 把一个公司比做一台电脑 ,那公司的总机号码就相当于ip地址, 每个员工的分机号就相当于端口, 你想找公司某个人,必须 先打电话到总机,然后再转分机 。
建立一个socket必须至少有2端, 一个服务端,一个客户端, 服务端被动等待并接收请求,客户端主动发起请求, 连接建立之后,双方可以互发数据。
基本参数
Socket Families(地址簇)
socket.
AF_UNIX 本机进程间通信
socket.
AF_INET IPV4(默认)
socket.
AF_INET6 IPV6
Socket Types(类型)
socket.
SOCK_STREAM 流式socket,代表TCP协议(默认)
socket.
SOCK_DGRAM 数据报式socket,代表UDP协议
socket方法
sk = socket.
socket
(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
建立socket连接对象
sk.bind(address)
s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。
sk.listen(backlog)
开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
这个值不能无限大,因为要在内核中维护连接队列
sk.setblocking(bool)
是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。
sk.accept()
接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
接收TCP 客户的连接(阻塞式)等待连接的到来
sk.connect(address)
连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
sk.connect_ex(address)
同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061
sk.close()
关闭套接字
sk.recv(bufsize[,flag])
接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。
sk.recvfrom(bufsize[.flag])
与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
sk.send(string[,flag])
将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
sk.sendall(string[,flag])
将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
内部通过递归调用send,将所有内容发送出去。
服务端步骤:
步骤:
1.server = socket.socket() 声明实例,生成连接对象
2.server.bind() 绑定要监听的端口
3.server.listen() 开始监听
4.conn,addr = server.accept()等待客户端发起连接,阻塞
5.接收数据(发送数据)
6.当客户端断开连接后,继续监听等待下一个客户端建立连接
……
关闭连接对象
代码示例:
import socket
"服务器端" server = socket.socket() # 生成连接对象
server.bind(("localhost",6000)) # 绑定要监听的端口
server.listen(5) # 开始监听(最大允许挂起的连接)
while True:
print("\n服务器在等待...")
conn,addr = server.accept() # 等待客户端发起建立连接,起到阻塞作用
# conn 是客户端链接过来而在服务器端为其生成的连接实例,addr是IP地址+端口
print("已成功连接")
print("连接对象:{0},地址:{1}\n".format(conn,addr))
while True:
try:
data = conn.recv(1024) # 接收数据
# (如果客服端断开连接,此步骤将会被无限循环操作,所以一定要有检查机制)
if data != b"": # 接收到的信号不是"000001"的话就正常执行
print("接收客户端信息:", data.decode())
msg = input(">>输入返回客户端的数据:")
conn.send(msg.encode(encoding="utf-8")) # 向客户端发送数据
else:
print("该客户已主动断开连接")
break
except ConnectionResetError as e:
print("该客户机异常!已被强迫断开连接",e)
break
else:
print("It's OK !") server.close()
客户端步骤:
步骤:
1.client = socket.socket() 声明实例,生成连接对象
2.client.connect() 与服务器建立连接
3.与服务器交互(发送接收数据)
4.client.close() 断开连接
代码示例:
import socket
"通信案例客户端消息接收与发送"
client = socket.socket() # 声明socket类型,并生成socket连接对象
try:
client.connect(("localhost",6000)) # 与服务器建立连接 while True:
msg = input(">>输入要向服务器发送的信息:")
client.send(msg.encode(encoding="utf-8")) # 向服务器发送信息(只能发送bytes字节类型,不能是str字符类型)
data = client.recv(1024) # 接收来自服务器的1024个字节
print("接收来自服务器的数据:",data.decode()) # 打印服务器发送的数据
chioce = input("按任意键继续,按0退出客户端")
if chioce == "":
client.send(b"") # 发送此信号表明客户端要断开连接
break
client.close()
except ConnectionRefusedError as e:
print("服务器还没开机!请静候")
需要注意的是:
1.客户端再发送数据时,要主要服务器接收的大小限制。如果超过了这个限制,超出的部分暂时存在系统缓冲区,第二次接收的时候再输出剩下的部分。例如服务器端的recv(1024),而客户端一次发了2024字节,那么剩下的1000字节存在缓冲区,第二次接收的时候会接收缓冲区的内容,将不会接收新发来的数据,会造成数据错误。官方建议一次性不超过8192字节
2.双发收发数据只能是bytes类型
3.粘包问题,下面讲
socket粘包问题
什么是粘包呢?我们知道发送数据,并且数据量比较大时,并不会一次性发送,即使能一次发送,客户端也不一定能一次性接收,所以服务器有个缓冲区,等客户端下次再接收数据的时候再发送给客户端,所以,就需要将数据分成几次发送,客户端分成几次接收。那又出来问题了,客户端知道数据(文件)有多大么?它怎么知道要接收几次?当然是要服务器告诉他啦!
于是,我们设计服务器首先发送数据大小(数据),再开始分批发送数据,客户端先接收文件大小(数据),再开始分批接收。问题就有可能在这里出现了。如果连续2次send数据,很有可能将两次的数据黏在一起发送出去,在客户端也无法将其分开,怎么办呢?
我们可以让服务器每次发送数据后,接收来自客户端的确认,这样会强制清空缓冲区,就不会造成粘包。当然,基于上面讲的方法,只需要在发送正式数据之前接收确认就好。
最后,如果希望100%确认双发收发数据是否一致,可以采用MD5校验。
服务器端步骤:
1.读取文件名
2.检测文件是否存在
3.打开文件
4.检测文件大小
5.将文件大小发给客户端
6.确认
7.开始边读边发(循环发送)
8.发送MD5
代码:
import socket,os,hashlib ser = socket.socket()
ser.bind(("localhost",5000))
ser.listen() while True:
try:
print("正在等待客户端连接...")
conn,addr = ser.accept()
print("已连接,new conn:",addr)
while True:
data = conn.recv(8192)
filename = data.decode()
print("寻找文件",filename)
if os.path.isfile(filename):
conn.send(b"")
conn.recv(1024)
f = open(filename,"rb")
m = hashlib.md5()
file_size = os.stat(filename).st_size
conn.send(str(file_size).encode(encoding="utf-8"))
client_ack = conn.recv(1024) # 接收确认信息
if client_ack == b"":
print("开始发送数据")
for line in f:
m.update(line)
conn.send(line)
f.close()
conn.send(m.hexdigest().encode(encoding="utf-8")) # 发送MD5值
else:
conn.send(b"") #表示文件不存在
print("该文件不存在!")
except ConnectionResetError:
print("该客户端已断开连接") ser.close()
print("服务器已关闭")
客户端步骤:
1.发送接收文件请求,同时将文件名发送给服务器
2.接收文件长度
3.本地新建同名文件,循环接收数据,并将其写入文件
4.同时更新本地MD5值
5.接收数据完毕后,再接收服务器的MD5值,与本地MD5值进行比较
代码:
import socket,hashlib def receive1(client):
"真正的数据接收"
while True:
res = b""
res = res + client.recv(1024)
return res def receive(client,filename):
"接收处理"
m = hashlib.md5()
rece_res_size = int(client.recv(1024).decode()) # 接收的结果长度,转成int型
client.send(b"")
rece_size = 0
res = b""
filename = filename.decode()
f = open(filename + ".new","wb")
while rece_size < rece_res_size:
if rece_res_size - rece_size >1024: # 如果不是最后一次接收数据
size = 1024
else: # 最后一次接收数据
size = rece_res_size - rece_size
a = client.recv(size) # 循环接收数据
res = res + a
rece_size = len(res)
m.update(a)
f.write(a)
print("发送数据量:{0},接收数据量:{1}".format(rece_res_size,rece_size))
else:
serves_md5 = client.recv(1024).decode()
print("服务器MD5:",serves_md5)
print("客户端MD5:",m.hexdigest())
if serves_md5 == m.hexdigest():
print("文件接收并校验完毕!")
res.decode()
f.close() def main():
client = socket.socket()
try:
client.connect(("localhost", 5000))
while True:
filename = input("请输入需要的文件名").strip().encode(encoding="utf-8")
if len(filename) == 0:
print("输入为空,重新输入")
continue
client.send(filename)
is_file = client.recv(1024)
if is_file == b"":
client.send(b"OK")
receive(client,filename) # 调用函数接收数据,返回结果res(bytes)
else:
print(" {0} 文件不存在!".format(filename.decode())) except ConnectionRefusedError:
print("等待服务器开机")
client.close() main()
socketserver
什么是socketserver?为什么需要它呢?
我们在前面的文章中普通的socket并不能同时处理多个客户端,当一个客户端在与服务器连接时,其它客户端只能排队。而sockerserver则不同,它可以并发地处理多个客户端请求。
import socketserver
'''
每一个客户端请求过来,都会实例化 MyTCPHandler
''' class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
"跟客户端所有的交互都是在handle里完成的"
while True:
try:
self.data = self.request.recv(1024).strip()
print("{0} wrote:".format(self.client_address[0]))
print(self.data)
if not self.data:
print("输入为空")
self.request.send(bytes("输入为空", "utf-8"))
else:
self.request.send(self.data.upper())
except ConnectionResetError :
print("客户已断开连接")
break if __name__ == "__main__":
HOST, PORT = "localhost", 9999
#server = socketserver.TCPServer((HOST, PORT), MyTCPHandler) # 实例化一对一的连接对象
server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler) # 实例化多并发的连接对象(多线程)
server.serve_forever()
客户端并没有什么区别:
import socket HOST, PORT = "localhost", 9999
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
while True:
data = input("输入字符")
try:
sock.sendall(bytes(data + "\n", "utf-8")) received = str(sock.recv(1024), "utf-8")
finally:
print("Sent: {0}".format(data))
print("Received: {0}".format(received)) sock.close()
初学Python——Socket网络编程的更多相关文章
- Python Socket 网络编程
Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...
- Python Socket 网络编程 (客户端的编程)
Socket 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的,例如我们每天浏览网页.QQ ...
- Python Socket网络编程详解
Socket 简介 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. s ...
- 25 python socket网络编程
一 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务端为你提供视频 ...
- Python - Socket网络编程 - 第二十六天
网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法. 高 ...
- python——socket网络编程
一.OSI七层模型
- python socket网络编程之粘包问题详解
一,粘包问题详情 1,只有TCP有粘包现象,UDP永远不会粘包 你的程序实际上无权直接操作网卡的,你操作网卡都是通过操作系统给用户程序暴露出来的接口,那每次你的程序要给远程发数据时,其实是先把数据从用 ...
- Python socket网络编程(通信介绍)
socket通信介绍 通信介绍(一) 1.所有网络协议的基础就是:socket 2.socket对TCP与UDP协议封装,让用户进行简单操作. 3.socket只做两件事:发 send,收 rec ...
- Python Socket 网络编程 (服务器端编程)
服务器端主要做以下工作: 打开 socket 绑定到特定的地址以及端口上 监听连接 建立连接 接收/发送数据 上面已经介绍了如何创建 socket 了,下面一步是绑定. 绑定 socket 函数 bi ...
随机推荐
- css:Media Queries: How to target desktop, tablet and mobile?
<!doctype html> <html> <head> <meta name="viewport" content="wid ...
- thinkphp 使用paginate分页搜索带参数
最近做项目发现使用paginate分页,搜索的时候点下一页搜索条件就变没了,所以在网上找了找一些方法,有的说是使用Page类,但是用习惯了paginate,再用Page不习惯,找到了一个方法,可以使用 ...
- vue从入门到进阶:计算属性computed与侦听器watch(三)
计算属性computed 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的.在模板中放入太多的逻辑会让模板过重且难以维护.例如: <div id="example" ...
- spring boot mybatis 打成可执行jar包后启动UnsatisfiedDependencyException异常
我的spring boot + mybatis项目在idea里面执行正常,但发布测试环境打成可执行jar包后就启动失败,提示错误如下: [ ERROR] [2018-08-30 17:23:48] o ...
- 多线程编程CompletableFuture与parallelStream
一.简介 平常在页面中我们会使用异步调用$.ajax()函数,如果是多个的话他会并行执行相互不影响,实际上Completable我理解也是和它类似,是java 8里面新出的异步实现类,Completa ...
- 也说Socket
网上有大量socket相关文章,茫茫多,大多交代不清,最近自我整理了一下socket相关知识,附加了大量代码注释,先看效果. 上代码,客户端: Socket socket1 = null;//一个全局 ...
- Codeup
问题 I: 习题5-10 分数序列求和 时间限制: 1 Sec 内存限制: 12 MB提交: 611 解决: 537[提交][状态][讨论版][命题人:外部导入] 题目描述 有如下分数序列 求出次 ...
- 将html前端代码提取公因数(5)
将html前端代码提取公因数(5) 注意:这是优化html代码,对于多个html代码相同的部分提取到一个模板中,只需要编写变化的html 1,利用Django提供的render方法的第三个参数的属性 ...
- Android Studio连接天天模拟器
方法:安装两者后,打开天天模拟器的adb.exe所在目录,我的是C:\Users\ Android\sdk\platform-tools,在打开的文件夹下使用“shift+鼠标右键”打开cmd终端. ...
- org.hibernate.NonUniqueObjectException
错误如下: 2017-3-29 15:17:52~ERROR~org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperV ...