一、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编程的更多相关文章

  1. python socket编程---从使用Python开发一个Socket示例说到开发者的思维和习惯问题

    今天主要说的是一个开发者的思维和习惯问题. 思维包括编程的思维和解决一个具体问题的分析思维,分析思路,分析方法,甚至是分析工具. 无论是好习惯还是不好的习惯,都是在者一天一天的思维中形成的.那些不好的 ...

  2. python基础之socket编程 (转自林海峰老师)

    python基础之socket编程   阅读目录 一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 ...

  3. Python 基础之socket编程(二)

    Python 基础之socket编程(二) 昨天只是对socket编程做了简单的介绍,只是把socket通信的框架搭建起来,要对其中的功能进行进一步的扩充,就来看看今天的料哈! 一.基于tcp的套接字 ...

  4. Python 基础之socket编程(三)

    python 基础之socket编程(三) 前面实现的基于socket通信只能实现什么呢?在tcp协议的通信中就是一个用户说一句,服务端给你回一句,你再给服务端说一句,服务端再给你回一句,就这样一直友 ...

  5. Python 基础之socket编程(一)

    Python 基础之socket编程(一) 可以进行通信玩儿了,感觉不错不错,网络通信就像打电话,我说一句你听一句之后,你再说一句,我听一句,就这样.....下去了.不扯淡了,来来来,看看今天都搞了点 ...

  6. python <12> socket 编程

    1.socket编程需要两个部分 服务器与客户端我们的python中调用 socket包就不需要自己写协议了(socket编程中windows 与Linux中的效果是完全不相同了,次代码最好是放在Li ...

  7. 十三python基础之socket编程

      阅读目录 一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 八 基于UDP的套接字 九 粘包现 ...

  8. python基础之socket编程

    一 客户端/服务器架构 二 osi七层 三 socket层 四 socket是什么 五 套接字发展史及分类 六 套接字工作流程 七 基于TCP的套接字 八 基于UDP的套接字 九 粘包现象 十 什么是 ...

  9. Python基础之socket编程(Day29)

    一.客户端/服务器架构 1.硬件c/s架构(打印机) 2.软件c/s架构 互联网中处处是c/s架构 浏览的网页就是如此 C/S架构与socket的关系 socket就是为了完成c/s架构的开发 二.s ...

随机推荐

  1. SQL Server 常用SQL

    --查询所有表 select * from sysobjects where xtype='u' ORDER BY name ASC

  2. event 事件1

    1.事件流 1.1 事件冒泡 IE8- 浏览器支持的事件流是事件冒泡.事件冒泡是事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)的过程. <!doctype html&g ...

  3. JAVA8 Stream集合操作:中间方法和完结方法

    StreamLambda为java8带了闭包,这一特性在集合操作中尤为重要:java8中支持对集合对象的stream进行函数式操作,此外,stream api也被集成进了collection api, ...

  4. Python的range()函数用法

    Python的range()函数有三种用法,简单地说就是下图的三种用法: 运行结果如下:

  5. 【转】Cisco交换机策略路由

    [转自]https://blog.csdn.net/kkfloat/article/details/39940623 1.概念 1)策略路由(PBR)是一种比基于目标网络进行路由更加灵活的数据包路由转 ...

  6. HNの野望

    1.标题 2.工作 3.学习 4.英语 5.健康 6.心理 7.绘画 8.看书

  7. JS判断图片是否加载完成 背景图404 快到碗里来

    面对这个问题 我最多做到表面笑嘻嘻 …… 真不知道测试怎么那么…… 啥都能给你测出来 有的没的都能给你测出来 算了算了  谁让本仙女本精灵本可爱温柔大方善解人意呢 …呵呵呵 ————————————正 ...

  8. 创建ResultUtils类

    1.在pom.xml中增加maven资源 <dependency> <groupId>com.alibaba</groupId> <artifactId> ...

  9. python ftp 传输文件

    # -*- coding: utf-8 -*- # 本地bytes 数据上报服务器同时创建文件from ftplib import FTP import time, _io from constant ...

  10. 00.pt-toolkit 目录

    一. 好用便利的工具,常用 pt-align 对齐文本格式pt-archiver 循序渐进的归档表,删除表,迁移数据pt-config-diff 对比不同配置文件.服务器配置参数pt-diskstat ...