Python开发——12.socket编程
一、OSI七层
1.物理层
物理层的主要功能是基于电气特性发送高低电压(高代表1,低代表0)形成电信号,使计算机完成组网以达到接入Internet的目的
2.数据链路层
数据链路层是用来定义电信号的分组方式,使单纯的电信号0和1变得有意义
(1)以太网协议
以太网协议(ethernet)是统一的分组标准,以太网协议规定:a.一组电信号构成一个数据包,叫做“帧”;b。每一数据帧分成报头head和数据data两部分
报头(head)固定为18个字节,发送者即源地址、接受者即目标地址和数据类型各占6个字节
数据(data)最短为46字节,最长为1500字节,它包含了数据包的具体内容
(2)mac地址和广播
ethernet规定接入Internet的设备都必须具备网卡,发送端和接收端的地址即网卡地址,又称mac地址。
ethernet采用广播的方式进行通信,一台主机通过ARP协议获取另一台主机的mac地址
3.网络层
网络层引入了一套新的地址即网络地址来区分不同的广播域
(1)IP协议
IP地址规定了网络地址由32位2进制表示,即从0.0.0.0-255.255.255.255,IP地址分为主机部分和网络部分
(2)子网掩码
子网掩码是描述子网络特征的参数,形式上与IP地址一致,但网络部分全部为1,主机部分全部为0,利用子网掩码与IP地址进行and运算(两个位数都为1运算结果为1,否则为0),结果相同表明在同一个子网络中。
(3)ARP协议
通过广播的方式发送数据包来获取目标主机的mac地址
4.传输层
传输层是用来建立端口到端口的通信,端口范围为0-65535,其中0-1023为系统占用端口
(1)tcp协议和udp协议
TCP协议为可靠传输,通常TCP的数据包的长度不会超过IP数据包的长度,理论上可以无限长;UDP协议为不可靠传输,总长度不超过65535个字节
(2)tcp协议的三次握手和四次挥手
5.应用层
用来规定开发的应用程序的数据格式
二、socket
1.客户端/服务端架构
即client/server(C/S)架构,socket就是为了实现C/S架构的开发,C/S架构的软件是基于网络进行通信的。
2.socket层
3.socket定义
socket是应用层与TCP/IP协议通信的中间层,是一组封装了TCP/IP协议的接口,用户遵循socket规定编程即遵循tcp/udp协议
socket主要有基于文件类型(AF_UNIX)和基于网络类型(AF_INET)两种家族。
4.工作流程
5.基于TCP的套接字
tcp是基于链接的,必须先启动服务端
服务端
ss = socket() #创建服务器套接字
ss.bind() #把地址绑定到套接字
ss.listen() #监听链接
inf_loop: #服务器无限循环
cs = ss.accept() #接受客户端链接
comm_loop: #通讯循环
cs.recv()/cs.send() #对话(接收与发送)
cs.close() #关闭客户端套接字
ss.close() #关闭服务器套接字(可选)
from socket import *
import subprocess
ip_port = ("10.10.27.37",8000)
back_log = 5
buffer_size = 1024 ftp_server = socket(AF_INET,SOCK_STREAM)
ftp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
ftp_server.bind(ip_port)
ftp_server.listen(back_log) while True:
conn,addr = ftp_server.accept()
print("新的客户端链接",addr)
while True:
try:
cmd = conn.recv(buffer_size)
print("收到客户端的命令",cmd)
if not cmd:
break
res = subprocess.Popen(cmd.decode("utf-8"),shell = True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
err = res.stderr.read()
if err:
cmd_res = err
else:
cmd_res = res.stdout.read()
conn.send(cmd_res)
except Exception:
break
conn.close()
ftp_server
客户端
cs = socket() # 创建客户套接字
cs.connect() # 尝试连接服务器
comm_loop: # 通讯循环
cs.send()/cs.recv() # 对话(发送/接收)
cs.close() # 关闭客户套接字
from socket import *
ip_port = ("10.10.27.37",8000)
buffer_size = 1024 ftp_client = socket(AF_INET,SOCK_STREAM)
ftp_client.connect(ip_port) while True:
cmd = input(">>>:")
if not cmd :
continue
if cmd == "quit":
break
ftp_client.send(cmd.encode("utf-8"))
res = ftp_client.recv(buffer_size)
print(res.decode("gbk"))
ftp_client.close()
ftp_client
6.基于udp的套接字
udp是无连接的,可以先启动任意一端
服务端
ss = socket() #创建一个服务器的套接字
ss.bind() #绑定服务器套接字
inf_loop: #服务器无限循环
cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
ss.close() # 关闭服务器套接字
from socket import *
ip_port = ("10.10.27.37",8001)
buffer_size = 1024 udp_server = socket(AF_INET,SOCK_DGRAM)
udp_server.bind(ip_port) while True:
data,addr =udp_sever.recvfrom(buffer_size)
print(data,addr)
udp_server.sendto(data.upper(),addr)
udp_server
客户端
cs = socket() # 创建客户套接字
comm_loop: # 通讯循环
cs.sendto()/cs.recvfrom() # 对话(发送/接收)
cs.close() # 关闭客户套接字
from socket import *
udp_client = socket(AF_INET,SOCK_DGRAM)
ip_port = ("10.10.27.37",8001)
buffer_size = 1024 while True:
msg = input(">>>:")
udp_client.sendto(msg.encode("utf-8"),ip_port)
data,addr = udp_client.recvfrom(buffer_size)
print(addr)
print(data.decode("utf-8"))
udp_client
7.粘包
(1)tcp和udp区别
- TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
- UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的
(2)粘包现象
粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
- 正常情况
- 发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
- 接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
(3)解决粘包的方法
发送时:先发报头长度,再编码报头内容然后发送,最后发真实内容。
接收时:先收报头长度,用struct取出来,根据取出的长度收取报头内容,然后解码,反序列化,从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容
from socket import *
import subprocess,struct
ip_port = ("10.10.27.37",666)
back_log = 5
buffer_size = 1024 tcp_server = socket(AF_INET,SOCK_STREAM)
tcp_server.bind(ip_port)
tcp_server.listen(back_log) while True:
conn,addr = tcp_server.accept()
print("新的客户端链接",addr)
while True:
#收
try:
cmd = conn.recv(buffer_size)
if not cmd :break
print("收到客户端的命令",cmd) #执行命令,得到的运行结果cmd_res
res = subprocess.Popen(cmd.decode("utf_8"),shell=True,
stderr = subprocess.PIPE,
stdout=subprocess.PIPE,
stdin=subprocess.PIPE)
err = res.stderr.read()
if err:
cmd_res = err
else:
cmd_res = res.stdout.read() #发
if not cmd_res:
cmd_res="执行成功".encode("gbk")
#解决粘包
length = len(cmd_res) data_length = struct.pack("i",length)#struct模块需要了解
conn.send(data_length)
conn.send(cmd_res)
except Exception:
break
解决粘包server端
from socket import *
import struct
from functools import partial
ip_port = ("10.10.27.37",666)
back_log = 5
buffer_size = 1024 tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port) while True:
cmd = input(">>>").strip()
if not cmd:continue
if cmd =="quit":break tcp_client.send(cmd.encode("utf-8"))
#解决粘包
length_data = tcp_client.recv(4)
length = struct.unpack("i",length_data)[0] # recv_msg ="".join(iter(partial(tcp_client.recv,buffer_size),b""))
recv_size = 0
recv_data = "b"
while recv_size<length:
recv_data=tcp_client.recv(buffer_size)
recv_size= len(recv_data)
print("命令的执行结果是",recv_data.decode("gbk"))
tcp_client.close
解决粘包client端
(4)TCP实现并发
基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环,利用socketserver模块中的两大类:server类(解决链接问题)和request类(解决通信问题)来实现并发
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
print("conn in:",self.request)#conn
print("addr is:",self.client_address)#addr while True:
#收消息
try:
data = self.request.recv(1024)
if not data:break
print("客户端收到的消息是:",data) #发消息
self.request.sendall(data.upper())
except Exception:
break
if __name__ == "__main__":
s = socketserver.ThreadingTCPServer(("10.10.27.37",8181),MyServer)#多进程
# s = socketserver.ForkingTCPServer(("10.10.27.37",8181),MyServer)#多线程,系统开销高于多进程 s.serve_forever()
server端
from socket import *
ip_port = ("10.10.27.37",8181)
back_log = 5
buffer_size = 1024 tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port) while True:
msg = input(">>>").strip()
if not msg:continue tcp_client.send(msg.encode("utf-8"))
data = tcp_client.recv(1024)
print("收到服务端发来的消息",data.decode("utf-8")) tcp_client.close
client端
Python开发——12.socket编程的更多相关文章
- python socket编程---从使用Python开发一个Socket示例说到开发者的思维和习惯问题
今天主要说的是一个开发者的思维和习惯问题. 思维包括编程的思维和解决一个具体问题的分析思维,分析思路,分析方法,甚至是分析工具. 无论是好习惯还是不好的习惯,都是在者一天一天的思维中形成的.那些不好的 ...
- python基础之socket编程 (转自林海峰老师)
python基础之socket编程 阅读目录 一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 ...
- Python 基础之socket编程(二)
Python 基础之socket编程(二) 昨天只是对socket编程做了简单的介绍,只是把socket通信的框架搭建起来,要对其中的功能进行进一步的扩充,就来看看今天的料哈! 一.基于tcp的套接字 ...
- Python 基础之socket编程(三)
python 基础之socket编程(三) 前面实现的基于socket通信只能实现什么呢?在tcp协议的通信中就是一个用户说一句,服务端给你回一句,你再给服务端说一句,服务端再给你回一句,就这样一直友 ...
- Python 基础之socket编程(一)
Python 基础之socket编程(一) 可以进行通信玩儿了,感觉不错不错,网络通信就像打电话,我说一句你听一句之后,你再说一句,我听一句,就这样.....下去了.不扯淡了,来来来,看看今天都搞了点 ...
- python <12> socket 编程
1.socket编程需要两个部分 服务器与客户端我们的python中调用 socket包就不需要自己写协议了(socket编程中windows 与Linux中的效果是完全不相同了,次代码最好是放在Li ...
- 十三python基础之socket编程
阅读目录 一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 八 基于UDP的套接字 九 粘包现 ...
- python基础之socket编程
一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 八 基于UDP的套接字 九 粘包现象 十 什么是 ...
- Python基础之socket编程(Day29)
一.客户端/服务器架构 1.硬件c/s架构(打印机) 2.软件c/s架构 互联网中处处是c/s架构 浏览的网页就是如此 C/S架构与socket的关系 socket就是为了完成c/s架构的开发 二.s ...
随机推荐
- 20175213 2018-2019-2 《Java程序设计》第9周学习总结
教材学习内容总结 根据课本的介绍下载了MySQL和Navicat for MySQL并成功对后者进行破解: 操作方法可分为三步: 一.配置程序--让我们程序能找到数据库的驱动jar包 1.把.jar文 ...
- 爬了招聘网站之后,总结Python学习的几点建议
来源商业新知网,原标题::爬了招聘网站之后,给你几点学习Python的建议 Python语言相关的岗位非常多,有运维,有自动化测试,有后端开发,有机器学习,如果想要快速上手,并且有不错的就业,那就推荐 ...
- 在create-react-app里使用ant design
使用create-react-app创建的项目,要使用ant design. 1.首先进入项目根目录,yarn add antd. 2.在App.css引入 样式文件,@import '~antd/d ...
- 大数据入门到精通19--mysql 数据导入到hive数据中
一.正常按照数据库和表导入 \\前面介绍了通过底层文件得形式导入到hive的表中,或者直接导入到hdfs中,\\现在介绍通过hive的database和table命令来从上层操作.sqoop impo ...
- Redis部署说明
一.普通部署 将Redis-x64-3.2.100解压,修改配置文件,一般不需要修改,直接使用默认,具体要修改可自行百度. 打开命令行,定位到解压目录,执行命令: redis-server.exe r ...
- 基于SDL2实现俄罗斯方块
俄罗斯方块有多种旋转规则,我这里采用的是SRS.如果要改变旋转规则的话也很方便. SRS: 内容后续补充.. 代码:https://github.com/CknightX/sdl_Tetris 素材来 ...
- C# 一些学习作业
下载地址:http://pan.baidu.com/s/1dEGCJdf 包括: 实现QQ旋转窗体功能 非“按角度旋转”,实现的是立体旋转. 实现QQ旋转窗体功能,窗口为不规则图像,打开时旋转180度 ...
- PHP 浮点型转整型的一个奇怪现象
起因 最近通过一个学长的题了解php弱类型的时候,发现了一个奇怪的现象. 正文 主要问题在这样一段代码: $c=(int)((0.1+$b)*10); 当$b=0.6,0.8以及其他值的时候都正常 将 ...
- Java中的属性和方法
题目 实体类 测试类
- 我的mac下有关php扩展的安装
之前安装yaf和mcrypt扩展一直失败,今天终于找到原因了.那是因为./configure的时候没有指定php版本,所以用了默认的php的版本,正确的姿势应该是:./configure --with ...