关键词:
socket:socket就是实现服务器和客户端数据的交换,服务器端接收并发送数据,客户端发送并接收数据,并且需要注意的是,在python3中,socket值接收字节。因为客户端在发送连接给服务器的时候,要转换为字节码;服务器端在返回给客户端的时候,也要转换为字节码。
如下所示:
服务器端:
import socket,os server = socket.socket() server.bind(("localhost",9999)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: data = conn.recv(1024) if not data: print("客户端已断开!") break print("执行指令:",data) cmd_res = os.popen(data.decode()).read() print("before send",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output....." conn.send(cmd_res.encode(‘utf-8‘)) print("send done") server.close()
上面是服务器端,使用os.popen()实现数据的处理,不过只能处理字符串,因此需要decode()成字符串格式,然后发送的时候要转换为字节码,encode()。
客户端:
import socket client = socket.socket() client.connect(("localhost",9999)) while True: cmd = input(">>:").strip() if len(cmd) == 0: continue client.send(cmd.encode(‘utf-8‘)) ‘‘‘服务器端发送为空,客户端是卡主的‘‘‘ cmd_res = client.recv(1024) print(cmd_res.decode()) client.close()
首先启动服务器端,然后启动客户端,如下所示:
服务器端发送数据:
>>:1 cmd has no output..... >>:dir build_server.py get_ip.py socket_server.py 客户端创建过程.py class_method.py lib s_server.py 类的方法.py class的方法.py property属性.py static_method.py 人类.py error_handle.py s_client.py 动态导入.py 上节内容 get_attr.py socket_client.py 反射.py >>:ls build_server.py class_method.py class的方法.py error_handle.py get_attr.py get_ip.py lib property属性.py s_client.py socket_client.py socket_server.py s_server.py static_method.py 动态导入.py 反射.py 客户端创建过程.py 类的方法.py 人类.py 上节内容
客户端接收数据:
new conn: (‘127.0.0.1‘, 55324) 执行指令: b‘1‘ before send 0 send done /bin/sh: 1: 1: not found 执行指令: b‘dir‘ before send 249 send done 执行指令: b‘ls‘ before send 219 send done
当客户端卡顿的时候,说明服务器端是没有数据发送过来的,因为客户端不能接收空的数据,服务器端也不能接收空的数据。这样就会造成卡顿的情况。
在接收的时候,会有一个缓存,缓存不满的时候,是不会发送数据的,客户端就接收不到数据。
由于缓存的存在,客户端接收数据有时候接收不完整,如何避免呢?要求服务器告诉客户端数据的大小,客户端根据数据的大小来接收数据。
收发一样大,告诉客户端接收数据的大小,如下:
服务器端:
import socket,os,time server = socket.socket() server.bind(("localhost",9998)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: data = conn.recv(1024) if not data: print("客户端已断开!") break print("执行指令:",data) cmd_res = os.popen(data.decode()).read() print("before send",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output....." conn.send(str(len(cmd_res)).encode(‘utf-8‘)) #先发送数据的大小给客户端 第一个send()发送数据 time.sleep(1) #连续发送数据会造成粘包现象,因此要有区分,不然容易粘包,这里让程序休眠一秒,先另外一个接收执行 conn.send(cmd_res.encode(‘utf-8‘)) #第二个send()发送数据 print("send done") server.close()
在服务器端上,我们计算了发送给客户端的数据大小,先把数据的大小告知客户端,接收多大的数据,然后在发送真正的数据给客户端。在数据发送的时候,要防止粘包,因为send()两次同时发送,造成粘包的情况,因此让程序休眠一秒,time.sleep(1),让客户端先接收数据,过了一秒重新接收数据,这样就不会粘包。
粘包情况如下;
>>:1 命令结果大小: b‘22cmd has no output.....‘ Traceback (most recent call last): File "/home/zhuzhu/第七天/s_client.py", line 15, in <module> while received_size < int(cmd_res_size.decode()): ValueError: invalid literal for int() with base 10: ‘22cmd has no output.....‘
客户端发送数据的时候,服务器返回的时候,由于两个send()同时发送数据造成粘包的情况,会出现错误。两条数据发送的时候连接到一起,造成粘包。两次数据发送链接到一起,为什么造成粘包,是因为程序执行的太快,客户端接收数据很快,速度赶在下一次发送之前。
客户端:
import socket client = socket.socket() client.connect(("localhost",9998)) while True: cmd = input(">>:").strip() if len(cmd) == 0: continue client.send(cmd.encode(‘utf-8‘)) ‘‘‘服务器端发送为空,客户端是卡主的‘‘‘ cmd_res_size = client.recv(1024) #接收命令结果的长度(服务器发送的) print("命令结果大小:",cmd_res_size) received_size = 0 while received_size < int(cmd_res_size.decode()): data = client.recv(1024) received_size += len(data) #每次收到的有可能小于1024,所以必须用len()判断 print(data.decode()) print(received_size) else: print("cmd res receive done......",received_size) # cmd_res = client.recv(1024) # # print(cmd_res.decode()) client.close()
在客户端中,我们先接收服务器发来的数据的大小,然后开始接收数据,当数据长度小于接收长度时,继续接收,一直等到没有数据接收为止。
在客户端中,接收的数据不一定等于规定的长度。并且要统一格式,我们知道,汉字和英文的长度是不一致的,汉字是由3个字节组成,而因为是由两个字节组成的。
首先启动客户端,然后启动服务器端,如下:
客户端发送数据: >>:1 命令结果大小: b‘22‘ cmd has no output..... 22 cmd res receive done...... 22 >>:ls 命令结果大小: b‘275‘ build_server.py class_method.py class的方法.py error_handle.py get_attr.py get_ip.py lib property属性.py s_client.py socket_client.py socket_server.py s_server.py static_method.py 动态导入.py 反射.py 客户端创建过程.py 类的方法.py 人类.py 上节内容 275 cmd res receive done...... 275 >>:dir 命令结果大小: b‘305‘ build_server.py get_ip.py socket_server.py 客户端创建过程.py class_method.py lib s_server.py 类的方法.py class的方法.py property属性.py static_method.py 人类.py error_handle.py s_client.py 动态导入.py 上节内容 get_attr.py socket_client.py 反射.py 305 cmd res receive done...... 305
从接收数据可以看出,如果有中文的话,接收数据的长度是不一致的,就是由于中文的字节是3个,因此都要转换为统一格式,现在调整服务器端,让服务器发送数据的长度的时候也是按照字节的形式发送长度,如下:
import socket,os,time server = socket.socket() server.bind(("localhost",9998)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: data = conn.recv(1024) if not data: print("客户端已断开!") break print("执行指令:",data) cmd_res = os.popen(data.decode()).read() print("before send",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output....." conn.send(str(len(cmd_res.encode(‘utf-8‘))).encode(‘utf-8‘)) #先发送数据的大小给客户端 time.sleep(1) #连续发送数据会造成粘包现象,因此要有区分,不然容易粘包,这里让程序休眠一秒,先另外一个接收执行 conn.send(cmd_res.encode(‘utf-8‘)) print("send done") server.close()
重新启动服务器,启动客户端发送数据,如下:
客户端输入指令: >>:1 命令结果大小: b‘22‘ cmd has no output..... 22 cmd res receive done...... 22 >>:ls 命令结果大小: b‘275‘ build_server.py class_method.py class的方法.py error_handle.py get_attr.py get_ip.py lib property属性.py s_client.py socket_client.py socket_server.py s_server.py static_method.py 动态导入.py 反射.py 客户端创建过程.py 类的方法.py 人类.py 上节内容 275 cmd res receive done...... 275 >>:dir 命令结果大小: b‘305‘ build_server.py get_ip.py socket_server.py 客户端创建过程.py class_method.py lib s_server.py 类的方法.py class的方法.py property属性.py static_method.py 人类.py error_handle.py s_client.py 动态导入.py 上节内容 get_attr.py socket_client.py 反射.py 305 cmd res receive done...... 305
从上面可以看出,当调整服务器端发送长度的方式之后,接收数据的长度和服务器告知客户端的长度是一致的。因此,在接收和发送数据的时候要以字节码方式统一计算长度,格式统一很重要,在socket中,计算长度统一格式为:字节码,发送和接收数据都是以字节码的形式操作。
socket网络编程,其实就是收发数据,只能收发字节的形式,要统一字节格式,要进行及时传唤,要告知客户端数据的大小。然后接收的时候,按照大小来接收。接收完成之后在继续进行执行程序。
粘包:两次发送数据粘到一起。
如何解决粘包:
(1)sleep(),停留一秒,time.sleep(0.5);
服务器端:
import socket,os,time server = socket.socket() server.bind(("localhost",9999)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: data = conn.recv(1024) if not data: print("客户端已断开!") break print("执行指令:",data) cmd_res = os.popen(data.decode()).read() print("before send",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output....." conn.send(str(len(cmd_res.encode(‘utf-8‘))).encode(‘utf-8‘)) #先发送数据的大小给客户端 time.sleep(1) #连续发送数据会造成粘包现象,因此要有区分,不然容易粘包,这里让程序休眠一秒,先另外一个接收执行 conn.send(cmd_res.encode(‘utf-8‘)) print("send done") server.close()
服务器两次发送数据的时间有间隔,这样就能避免数据发送粘包的情况。不过使用sleep()太low了。
(2)第一次发送之后,接收数据,接收客户端发来的第一次数据发送接收成功的消息,进行第二次发送,这样就能隔断数据的发送
服务器端:
import socket,os,time server = socket.socket() server.bind(("localhost",9998)) server.listen() while True: conn,addr = server.accept() print("new conn:",addr) while True: data = conn.recv(1024) if not data: print("客户端已断开!") break print("执行指令:",data) cmd_res = os.popen(data.decode()).read() print("before send",len(cmd_res)) if len(cmd_res) == 0: cmd_res = "cmd has no output....." conn.send(str(len(cmd_res.encode(‘utf-8‘))).encode(‘utf-8‘)) #先发送数据的大小给客户端 # time.sleep(1) #连续发送数据会造成粘包现象,因此要有区分,不然容易粘包,这里让程序休眠一秒,先另外一个接收执行 clicent_ack = conn.recv(1024) #等待接收数据,让下面的发送send()暂时不执行,要收到客户端的数据大小进行响应。wait to confirm print("ack from client:",clicent_ack) conn.send(cmd_res.encode(‘utf-8‘)) print("send done") server.close()
服务器端,要想防止粘包,则在两次发送数据直接增加一个第一次发送数据成功的确认,即接收成功发送数据的指令,如上面所示,conn.recv()其实也没有什么功能,就是隔断第二次发送的执行时间,让第二次发送数据在第一次执行完成之后再进行执行;
客户端:
import socket client = socket.socket() client.connect(("localhost",9998)) while True: cmd = input(">>:").strip() if len(cmd) == 0: continue client.send(cmd.encode(‘utf-8‘)) ‘‘‘服务器端发送为空,客户端是卡主的‘‘‘ cmd_res_size = client.recv(1024) #接收命令结果的长度(服务器发送的) print("命令结果大小:",cmd_res_size) client.send("准备好接收了,loser,可以发了".encode(‘utf-8‘)) received_size = 0 while received_size < int(cmd_res_size.decode()): data = client.recv(1024) received_size += len(data) #每次收到的有可能小于1024,所以必须用len()判断 print(data.decode()) print(received_size) else: print("cmd res receive done......",received_size) # cmd_res = client.recv(1024) # # print(cmd_res.decode()) client.close()
客户端上,数据接收完成之后,发送一条指令,即让服务器接着发送第二条指令。第一条指令是发送数据的大小,第二条指令是发送数据;
上面就解决了粘包的情况。
其实,socket在发送和就是数据的时候都是同步的,要想隔断,只能进行成功验证,先隔断不让后面send()执行,只有第一次发送成功之后,并且通过验证,再来进行第二次执行。
day8-socket网络编程
本节内容:Socket语法及相关SocketServer实现多并发 Socket语法及相关   查看详情
day8--socket回顾
... 后面学习了线程、协成和异步,它们的框架都是基于socket的协议,基本原理都是一样的,现在把这几个模块重温一下,尽量掌握这些知识更全面一些。 动态导入模块,知道知道模块名,可以像反射一样,使用字... 查看详情
day8--socket文件传输
FTPserver1、读取文件名2、检测文件是否存在3、打开文件4、检测文件大小(告诉客户端发送文件的大小)5、发送文件大小和MD5值给客户端,MD56、等待客户端确认(防止粘包)7、开始边读边发数据8、发送完整的MD59、关闭服务器&nb... 查看详情
java编程高级进阶
Java内存模型对hadoopnamenode-format执行过程的探究 查看详情
java进阶网络编程原来如此有趣
文章目录前言网络编程软件构成网络编程三要素通讯协议TCP/IP协议两种通讯协议IP地址端口号Java网络编程服务端服务器端代码展示客户端客户端代码展示效果展示结语前言 查看详情
linux-网络通信-进阶
//FileDownloadThroughServer.cpp:Definestheentrypointfortheconsoleapplication./*----------------------------------------------------------------------------------------------------------功能:建立C/S 查看详情
2.1go微服务实战(go语言进阶)---并发编程进阶
第8章 并发编程进阶8.1 竞态与并发模式 8.1.1 数据竞态 8.1.2 并发原理8.2 sync包 8.2.1 sync.Mutex互斥锁 8.2.2 sync.RWMutex多读写锁 8.2.3 sync.Once 8.2.4 sync.Cond 8.2.5 sync.Pool 8.2.6 sync.Map8.3 context包 8.3.1 应用场景 8.3.2 定义 8.3.3... 查看详情
spark函数式编程进阶
函数式编程进阶1、函数和变量一样作为Scala语言的一等公民,函数可以直接复制给变量;2、函数更长用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称,但是匿名函数赋值给一个变量(其... 查看详情
socket网络编程进阶与实战
...行本课程的学习。...1-1课程导学(课前必看)第2章Socket网络编程快速入门本章首先整体介绍什么是Socket网络编程;让大家对Sock 查看详情
socket网络编程进阶与实战
...于希望极大的方便同学进行本课程的学习。...第2章Socket网络编程快速入门本章首先整体介绍什么是Socket网络编程;让大家对Socket有个大概的概念与方向。之后通过几 查看详情
socket网络编程进阶与实战
...于希望极大的方便同学进行本课程的学习。...第2章Socket网络编程快速入门本章首先整体介绍什么是Socket网络编程;让大家对Socket有个大概的概念与方向。之后通过几 查看详情
python进阶之网络编程(代码片段)
...通信使用网络的目的把多方链接在一起,进行数据传递;网络编程就是,让不同电脑上的软件进行数据传递,即进程间通信;ip地址ip地址概念和作用IP地址是什么:比如192.168.1.1这样的一些数字;ip地址的作用:用来在电脑中标... 查看详情
java网络编程----tdp进阶篇上传文本文件
设计需求:从客户端上传txt文件到服务器,服务端收到文件后,发送消息给客户端接收完成。1.服务器端:publicclassUpLoadFileServer{publicstaticvoidmain(String[]args)throwsException{ ServerSocketss=newServerSocket(10010); Sockets=ss.accept();BufferedRe 查看详情
socket编程进阶(代码片段)
socketserver虽说用Python编写简单的网络程序很方便,但复杂一点的网络程序还是用现成的框架比较好。这样就可以专心事务逻辑,而不是套接字的各种细节。SocketServer模块简化了编写网络服务程序的任务。同时SocketServer模块也是Pyt... 查看详情
scala函数式编程进阶
1packagecom.dtspark.scala.basics23/**4*函数式编程进阶:5*1,函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量;6*2,函数更长用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称,但... 查看详情
python面向对象编程进阶(代码片段)
python面向对象编程进阶 一.isinstance(obj,cls)和issubclass(sub,super)isinstance(obj,cls)检查是否obj是否是类cls的对象1classFoo(object):2pass34obj=Foo()56isinstance(obj,Foo)issubclass(sub,super)检查sub类是否是super类的派生类1class 查看详情
shell的进阶编程(代码片段)
shell的进阶编程关于for for变量名字in列表;do 循环体 done 例如for:forNAME[inWORDS...];doCOMMANDS;don其中前面的name就是个变量名,而且不需要加$,每次循环和中间列表中的值有关,中间的列表可... 查看详情
java进阶(10)-网络编程
BIO、NIO、AIONIOvsIO之间的理念上面的区别(NIO将阻塞交给了后台线程执行)IO是面向流的,NIO是面向缓冲区的JavaIO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方;NIO则能前后移动流中... 查看详情