整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          一篇文章搞定網(wǎng)絡(luò)編程中的黏包現(xiàn)象

          一篇文章搞定網(wǎng)絡(luò)編程中的黏包現(xiàn)象

          包現(xiàn)象

          基于tcp先制作一個(gè)遠(yuǎn)程執(zhí)行命令的程序(命令ls -l ; lllllll ; pwd)

          注意

          res=subprocess.Popen(cmd.decode('utf-8'),
          shell=True,
          stderr=subprocess.PIPE,
          stdout=subprocess.PIPE)
          
          的結(jié)果的編碼是以當(dāng)前所在的系統(tǒng)為準(zhǔn)的,如果是windows,那么res.stdout.read()讀出的就是GBK編碼的,在接收端需要用GBK解碼
          
          且只能從管道里讀一次結(jié)果

          同時(shí)執(zhí)行多條命令之后,得到的結(jié)果很可能只有一部分,在執(zhí)行其他命令的時(shí)候又接收到之前執(zhí)行的另外一部分結(jié)果,這種顯現(xiàn)就是黏包。

          基于tcp協(xié)議實(shí)現(xiàn)的黏包

          tcp - server

          from socket import *
          import subprocess
          
          ip_port=('127.0.0.1',8888)
          BUFSIZE=1024
          
          tcp_socket_server=socket(AF_INET,SOCK_STREAM)
          tcp_socket_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
          tcp_socket_server.bind(ip_port)
          tcp_socket_server.listen(5)
          
          while True:
              conn,addr=tcp_socket_server.accept()
              print('客戶端',addr)
          
              while True:
                  cmd=conn.recv(BUFSIZE)
                  if len(cmd)==0:break
          
                  res=subprocess.Popen(cmd.decode('utf-8'),shell=True,
                                   stdout=subprocess.PIPE,
                                   stdin=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
          
                  stderr=res.stderr.read()
                  stdout=res.stdout.read()
                  conn.send(stderr)
                  conn.send(stdout)

          udp - client

          from socket import *
          ip_port=('127.0.0.1',9000)
          bufsize=1024
          
          udp_client=socket(AF_INET,SOCK_DGRAM)
          
          
          while True:
              msg=input('>>: ').strip()
              udp_client.sendto(msg.encode('utf-8'),ip_port)
              err,addr=udp_client.recvfrom(bufsize)
              out,addr=udp_client.recvfrom(bufsize)
              if err:
                  print('error : %s'%err.decode('utf-8'),end='')
              if out:
                  print(out.decode('utf-8'), end='')

          注意:只有TCP有粘包現(xiàn)象,UDP永遠(yuǎn)不會(huì)粘包

          黏包成因

          TCP協(xié)議中的數(shù)據(jù)傳遞

          當(dāng)發(fā)送端緩沖區(qū)的長度大于網(wǎng)卡的MTU時(shí),tcp會(huì)將這次發(fā)送的數(shù)據(jù)拆成幾個(gè)數(shù)據(jù)包發(fā)送出去。

          MTU是Maximum Transmission Unit的縮寫。意思是網(wǎng)絡(luò)上傳送的最大數(shù)據(jù)包。MTU的單位是字節(jié)。

          大部分網(wǎng)絡(luò)設(shè)備的MTU都是1500。如果本機(jī)的MTU比網(wǎng)關(guān)的MTU大,大的數(shù)據(jù)包就會(huì)被拆開來傳送,

          這樣會(huì)產(chǎn)生很多數(shù)據(jù)包碎片,增加丟包率,降低網(wǎng)絡(luò)速度。

          面向流的通信特點(diǎn)和Nagle算法

          TCP(transport control protocol,傳輸控制協(xié)議)是面向連接的,面向流的,提供高可靠性服務(wù)。

          收發(fā)兩端(客戶端和服務(wù)器端)都要有一個(gè)成對(duì)的socket,因此,發(fā)送端為了將多個(gè)發(fā)往接收端的包,更有效的發(fā)到對(duì)方,使用了優(yōu)化方法(Nagle算法),將多次間隔較小且數(shù)據(jù)量小的數(shù)據(jù),合并成一個(gè)大的數(shù)據(jù)塊,然后進(jìn)行封包。

          這樣,接收端,就難于分辨出來了,必須提供科學(xué)的拆包機(jī)制。 即面向流的通信是無消息保護(hù)邊界的。

          對(duì)于空消息:tcp是基于數(shù)據(jù)流的,于是收發(fā)的消息不能為空,這就需要在客戶端和服務(wù)端都添加空消息的處理機(jī)制,防止程序卡住,而udp是基于數(shù)據(jù)報(bào)的,即便是你輸入的是空內(nèi)容(直接回車),也可以被發(fā)送,udp協(xié)議會(huì)幫你封裝上消息頭發(fā)送過去。

          可靠黏包的tcp協(xié)議:tcp的協(xié)議數(shù)據(jù)不會(huì)丟,沒有收完包,下次接收,會(huì)繼續(xù)上次繼續(xù)接收,己端總是在收到ack時(shí)才會(huì)清除緩沖區(qū)內(nèi)容。數(shù)據(jù)是可靠的,但是會(huì)粘包。

          基于tcp協(xié)議特點(diǎn)的黏包現(xiàn)象成因


          發(fā)送端可以是一K一K地發(fā)送數(shù)據(jù),而接收端的應(yīng)用程序可以兩K兩K地提走數(shù)據(jù),當(dāng)然也有可能一次提走3K或6K數(shù)據(jù),或者一次只提走幾個(gè)字節(jié)的數(shù)據(jù)。

          也就是說,應(yīng)用程序所看到的數(shù)據(jù)是一個(gè)整體,或說是一個(gè)流(stream),一條消息有多少字節(jié)對(duì)應(yīng)用程序是不可見的,因此TCP協(xié)議是面向流的協(xié)議,這也是容易出現(xiàn)粘包問題的原因。

          而UDP是面向消息的協(xié)議,每個(gè)UDP段都是一條消息,應(yīng)用程序必須以消息為單位提取數(shù)據(jù),不能一次提取任意字節(jié)的數(shù)據(jù),這一點(diǎn)和TCP是很不同的。

          怎樣定義消息呢?可以認(rèn)為對(duì)方一次性write/send的數(shù)據(jù)為一個(gè)消息,需要明白的是當(dāng)對(duì)方send一條信息的時(shí)候,無論底層怎樣分段分片,TCP協(xié)議層會(huì)把構(gòu)成整條消息的數(shù)據(jù)段排序完成后才呈現(xiàn)在內(nèi)核緩沖區(qū)。

          UDP不會(huì)發(fā)生黏包

          UDP(user datagram protocol,用戶數(shù)據(jù)報(bào)協(xié)議)是無連接的,面向消息的,提供高效率服務(wù)。

          不會(huì)使用塊的合并優(yōu)化算法,, 由于UDP支持的是一對(duì)多的模式,所以接收端的skbuff(套接字緩沖區(qū))采用了鏈?zhǔn)浇Y(jié)構(gòu)來記錄每一個(gè)到達(dá)的UDP包,在每個(gè)UDP包中就有了消息頭(消息來源地址,端口等信息),這樣,對(duì)于接收端來說,就容易進(jìn)行區(qū)分處理了。 即面向消息的通信是有消息保護(hù)邊界的。

          對(duì)于空消息:tcp是基于數(shù)據(jù)流的,于是收發(fā)的消息不能為空,這就需要在客戶端和服務(wù)端都添加空消息的處理機(jī)制,防止程序卡住,而udp是基于數(shù)據(jù)報(bào)的,即便是你輸入的是空內(nèi)容(直接回車),也可以被發(fā)送,udp協(xié)議會(huì)幫你封裝上消息頭發(fā)送過去。

          不可靠不黏包的udp協(xié)議:udp的recvfrom是阻塞的,一個(gè)recvfrom(x)必須對(duì)唯一一個(gè)sendinto(y),收完了x個(gè)字節(jié)的數(shù)據(jù)就算完成,若是y;x數(shù)據(jù)就丟失,這意味著udp根本不會(huì)粘包,但是會(huì)丟數(shù)據(jù),不可靠。

          補(bǔ)充說明

          用UDP協(xié)議發(fā)送時(shí),用sendto函數(shù)最大能發(fā)送數(shù)據(jù)的長度為:65535- IP頭(20) – UDP頭(8)=65507字節(jié)。用sendto函數(shù)發(fā)送數(shù)據(jù)時(shí),如果發(fā)送數(shù)據(jù)長度大于該值,則函數(shù)會(huì)返回錯(cuò)誤。(丟棄這個(gè)包,不進(jìn)行發(fā)送)

          用TCP協(xié)議發(fā)送時(shí),由于TCP是數(shù)據(jù)流協(xié)議,因此不存在包大小的限制(暫不考慮緩沖區(qū)的大小),這是指在用send函數(shù)時(shí),數(shù)據(jù)長度參數(shù)不受限制。而實(shí)際上,所指定的這段數(shù)據(jù)并不一定會(huì)一次性發(fā)送出去,如果這段數(shù)據(jù)比較長,會(huì)被分段發(fā)送,如果比較短,可能會(huì)等待和下一次數(shù)據(jù)一起發(fā)送。

          會(huì)發(fā)生黏包的兩種情況

          情況一 發(fā)送方的緩存機(jī)制

            發(fā)送端需要等緩沖區(qū)滿才發(fā)送出去,造成粘包(發(fā)送數(shù)據(jù)時(shí)間間隔很短,數(shù)據(jù)了很小,會(huì)合到一起,產(chǎn)生粘包)

          服務(wù)器

          服務(wù)器from socket import *
          ip_port=('127.0.0.1',8080)
          
          tcp_socket_server=socket(AF_INET,SOCK_STREAM)
          tcp_socket_server.bind(ip_port)
          tcp_socket_server.listen(5)
          
          
          conn,addr=tcp_socket_server.accept()
          
          
          data1=conn.recv(10)
          data2=conn.recv(10)
          
          print('----->',data1.decode('utf-8'))
          print('----->',data2.decode('utf-8'))
          
          conn.close()

          客戶端

          import socket
          BUFSIZE=1024
          ip_port=('127.0.0.1',8080)
          
          s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
          res=s.connect_ex(ip_port)
          
          
          s.send('hello tom'.encode('utf-8'))

          總結(jié)

            黏包現(xiàn)象只發(fā)生在tcp協(xié)議中:

            1.從表面上看,黏包問題主要是因?yàn)榘l(fā)送方和接收方的緩存機(jī)制、tcp協(xié)議面向流通信的特點(diǎn)。

            2.實(shí)際上,主要還是因?yàn)榻邮辗讲恢老⒅g的界限,不知道一次性提取多少字節(jié)的數(shù)據(jù)所造成的

          黏包的解決方案

          解決方案一

            問題的根源在于,接收端不知道發(fā)送端將要傳送的字節(jié)流的長度,所以解決粘包的方法就是圍繞,如何讓發(fā)送端在發(fā)送數(shù)據(jù)前,把自己將要發(fā)送的字節(jié)流總大小讓接收端知曉,然后接收端來一個(gè)死循環(huán)接收完所有數(shù)據(jù)


          服務(wù)端

          import socket,subprocess
          ip_port=('127.0.0.1',8080)
          s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
          s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
          
          s.bind(ip_port)
          s.listen(5)
          
          while True:
              conn,addr=s.accept()
              print('客戶端',addr)
              while True:
                  msg=conn.recv(1024)
                  if not msg:break
                  res=subprocess.Popen(msg.decode('utf-8'),shell=True,\
                                      stdin=subprocess.PIPE,\
                                   stderr=subprocess.PIPE,\
                                   stdout=subprocess.PIPE)
                  err=res.stderr.read()
                  if err:
                      ret=err
                  else:
                      ret=res.stdout.read()
                  data_length=len(ret)
                  conn.send(str(data_length).encode('utf-8'))
                  data=conn.recv(1024).decode('utf-8')
                  if data=='recv_ready':
                      conn.sendall(ret)
              conn.close()

          客戶端

          import socket,time
          s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
          res=s.connect_ex(('127.0.0.1',8080))
          
          while True:
              msg=input('>>: ').strip()
              if len(msg)==0:continue
              if msg=='quit':break
          
              s.send(msg.encode('utf-8'))
              length=int(s.recv(1024).decode('utf-8'))
              s.send('recv_ready'.encode('utf-8'))
              send_size=0
              recv_size=0
              data=b''
              while recv_size < length:
                  data+=s.recv(1024)
                  recv_size+=len(data)
          
          
              print(data.decode('utf-8'))

          存在的問題

          程序的運(yùn)行速度遠(yuǎn)快于網(wǎng)絡(luò)傳輸速度,所以在發(fā)送一段字節(jié)前,先用send去發(fā)送該字節(jié)流長度,這種方式會(huì)放大網(wǎng)絡(luò)延遲帶來的性能損耗

          解決方案進(jìn)階

          剛剛的方法,問題在于我們我們在發(fā)送

            我們可以借助一個(gè)模塊,這個(gè)模塊可以把要發(fā)送的數(shù)據(jù)長度轉(zhuǎn)換成固定長度的字節(jié)。這樣客戶端每次接收消息之前只要先接受這個(gè)固定長度字節(jié)的內(nèi)容看一看接下來要接收的信息大小,那么最終接受的數(shù)據(jù)只要達(dá)到這個(gè)值就停止,就能剛好不多不少的接收完整的數(shù)據(jù)了。

          struct模塊

            該模塊可以把一個(gè)類型,如數(shù)字,轉(zhuǎn)成固定長度的bytes

          >>> struct.pack('i',1111111111111)
          
          struct.error: 'i' format requires -2147483648 <=number <=2147483647 #這個(gè)是范圍


          import json,struct
          #假設(shè)通過客戶端上傳1T:1073741824000的文件a.txt
          
          #為避免粘包,必須自定制報(bào)頭
          header={'file_size':1073741824000,'file_name':'/a/b/c/d/e/a.txt','md5':'8f6fbf8347faa4924a76856701edb0f3'} #1T數(shù)據(jù),文件路徑和md5值
          
          #為了該報(bào)頭能傳送,需要序列化并且轉(zhuǎn)為bytes
          head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并轉(zhuǎn)成bytes,用于傳輸
          
          #為了讓客戶端知道報(bào)頭的長度,用struck將報(bào)頭長度這個(gè)數(shù)字轉(zhuǎn)成固定長度:4個(gè)字節(jié)
          head_len_bytes=struct.pack('i',len(head_bytes)) #這4個(gè)字節(jié)里只包含了一個(gè)數(shù)字,該數(shù)字是報(bào)頭的長度
          
          #客戶端開始發(fā)送
          conn.send(head_len_bytes) #先發(fā)報(bào)頭的長度,4個(gè)bytes
          conn.send(head_bytes) #再發(fā)報(bào)頭的字節(jié)格式
          conn.sendall(文件內(nèi)容) #然后發(fā)真實(shí)內(nèi)容的字節(jié)格式
          
          #服務(wù)端開始接收
          head_len_bytes=s.recv(4) #先收?qǐng)?bào)頭4個(gè)bytes,得到報(bào)頭長度的字節(jié)格式
          x=struct.unpack('i',head_len_bytes)[0] #提取報(bào)頭的長度
          
          head_bytes=s.recv(x) #按照?qǐng)?bào)頭長度x,收取報(bào)頭的bytes格式
          header=json.loads(json.dumps(header)) #提取報(bào)頭
          
          #最后根據(jù)報(bào)頭的內(nèi)容提取真實(shí)的數(shù)據(jù),比如
          real_data_len=s.recv(header['file_size'])
          s.recv(real_data_len)

          關(guān)于struct的詳細(xì)用法

          #_*_coding:utf-8_*_
          #http://www.cnblogs.com/coser/archive/2011/12/17/2291160.html
          __author__='Linhaifeng'
          import struct
          import binascii
          import ctypes
          
          values1=(1, 'abc'.encode('utf-8'), 2.7)
          values2=('defg'.encode('utf-8'),101)
          s1=struct.Struct('I3sf')
          s2=struct.Struct('4sI')
          
          print(s1.size,s2.size)
          prebuffer=ctypes.create_string_buffer(s1.size+s2.size)
          print('Before : ',binascii.hexlify(prebuffer))
          # t=binascii.hexlify('asdfaf'.encode('utf-8'))
          # print(t)
          
          
          s1.pack_into(prebuffer,0,*values1)
          s2.pack_into(prebuffer,s1.size,*values2)
          
          print('After pack',binascii.hexlify(prebuffer))
          print(s1.unpack_from(prebuffer,0))
          print(s2.unpack_from(prebuffer,s1.size))
          
          s3=struct.Struct('ii')
          s3.pack_into(prebuffer,0,123,123)
          print('After pack',binascii.hexlify(prebuffer))
          print(s3.unpack_from(prebuffer,0))

          使用struct解決黏包

          借助struct模塊,我們知道長度數(shù)字可以被轉(zhuǎn)換成一個(gè)標(biāo)準(zhǔn)大小的4字節(jié)數(shù)字。因此可以利用這個(gè)特點(diǎn)來預(yù)先發(fā)送數(shù)據(jù)長度。


          服務(wù)端自定制報(bào)頭

          import socket,struct,json
          import subprocess
          phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
          phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
          
          phone.bind(('127.0.0.1',8080))
          
          phone.listen(5)
          
          while True:
              conn,addr=phone.accept()
              while True:
                  cmd=conn.recv(1024)
                  if not cmd:break
                  print('cmd: %s' %cmd)
          
                  res=subprocess.Popen(cmd.decode('utf-8'),
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
                  err=res.stderr.read()
                  print(err)
                  if err:
                      back_msg=err
                  else:
                      back_msg=res.stdout.read()
          
          
                  conn.send(struct.pack('i',len(back_msg))) #先發(fā)back_msg的長度
                  conn.sendall(back_msg) #在發(fā)真實(shí)的內(nèi)容
          
              conn.close()

          客戶端

          from socket import *
          import struct,json
          
          ip_port=('127.0.0.1',8080)
          client=socket(AF_INET,SOCK_STREAM)
          client.connect(ip_port)
          
          while True:
              cmd=input('>>: ')
              if not cmd:continue
              client.send(bytes(cmd,encoding='utf-8'))
          
              head=client.recv(4)
              head_json_len=struct.unpack('i',head)[0]
              head_json=json.loads(client.recv(head_json_len).decode('utf-8'))
              data_len=head_json['data_size']
          
              recv_size=0
              recv_data=b''
              while recv_size < data_len:
                  recv_data+=client.recv(1024)
                  recv_size+=len(recv_data)
          
              print(recv_data.decode('utf-8'))
              #print(recv_data.decode('gbk')) #windows默認(rèn)gbk編碼

          #網(wǎng)絡(luò)通信##網(wǎng)絡(luò)##python##Python基礎(chǔ)##科技新星創(chuàng)作營#

          焊機(jī)在點(diǎn)焊鋁和鋁合金時(shí),或在點(diǎn)焊涂覆有鋅、鎘、鉛等低熔點(diǎn)金屬的鋼板時(shí),電極的粘附現(xiàn)象比變形還嚴(yán)重,例如,用鋯銅或鉻銅電極點(diǎn)焊鋁合金板,不到50點(diǎn)焊件表面已發(fā)黑,電極出現(xiàn)粘附,但是從允許電極工作面直徑變形量增加20%計(jì)算。

          那么至少可以焊接數(shù)千個(gè)焊點(diǎn),用鉻銅合金電極點(diǎn)焊1.3mmLF3鋁合金時(shí)出現(xiàn)粘附的焊點(diǎn)數(shù),電極出現(xiàn)粘附現(xiàn)象雖有先后,但是都在焊接幾十個(gè)點(diǎn)的范圍內(nèi)發(fā)生粘附,在焊接鍍鋅鋼板時(shí)電極的粘附和變形多同時(shí)發(fā)生。

          焊接鍍鋅鋼板時(shí)電極出現(xiàn)粘附的原因,首先是因電極接觸到鍍鋅層熔點(diǎn)低、硬度低。電導(dǎo)率高,在電極力的作用下,接觸面積迅速擴(kuò)大,接觸電阻降低,測試數(shù)據(jù)表明,鍍鋅鋼板的接觸電阻只有普通低碳鋼板的1/10-1/20,其次,焊接電流一經(jīng)過焊件,低熔點(diǎn)的鋅層最先熔化,并立即填滿了電極的接觸部位和板隙,使導(dǎo)電面積擴(kuò)大,電流密度減低。

          因此,焊接鍍鋅鋼板一般都要用大電流強(qiáng)冷卻的焊接規(guī)范,和低碳鋼相比,焊接電流要增加30%-50%,電極力增加10%-30%,焊接時(shí)間不變或減少20%,有時(shí)也可在電流和電極力不變的條件下,適當(dāng)延長焊接時(shí)間,由于焊接電流與電極力的增加,或是焊接時(shí)間增加,都會(huì)使電極頭部的發(fā)熱和變形更加嚴(yán)重。

          在電極工作面上強(qiáng)烈粘附上鋅層,粘附在電極上的鋅在高溫下會(huì)向銅擴(kuò)散,生成銅合金,使電極表面的導(dǎo)電性能變差,硬度降低,變形增加,而增大了的電極表面又會(huì)與更多的鋅層接觸,形成惡性循環(huán),因而必須采用強(qiáng)制冷卻,增加電極的修銼頻率。

          電極的粘附現(xiàn)象與電極的材料和頭部的形狀有關(guān),在相同的焊接電流條件下,彌散強(qiáng)化銅出現(xiàn)粘附的焊接時(shí)間最長,鉻銅出現(xiàn)粘附的焊接時(shí)間最短,而純銅和鉻銅是介于上述兩者之間,有資料表明,焊接鋁合金時(shí),用銀銅和鎂硼銅電極焊接500點(diǎn),焊件表面才會(huì)發(fā)黑。

          鉻銅電極焊接300點(diǎn),而用鋯銅和鉻鋯銅電極只能焊50點(diǎn),這個(gè)現(xiàn)象反應(yīng)了不同電導(dǎo)率的電極銅合金具有不同焊接效果,電導(dǎo)率高的電極與焊件間的接觸電阻小,焊接發(fā)熱小。

          反之,電導(dǎo)率低的電極,與焊件的接觸電阻大,焊接發(fā)熱嚴(yán)重,此外,電極合金的晶粒組織大小也會(huì)對(duì)粘附產(chǎn)生影響,在焊接過程中,由于高溫和焊接力的作用,電極合金的組織會(huì)發(fā)生變化,此時(shí)粗晶粒組織要比細(xì)晶粒組織的嚴(yán)重,實(shí)驗(yàn)證明,焊接鋁合金時(shí)電極表面溫度只有673-723K,此時(shí)細(xì)晶粒組織的電極合金更能穩(wěn)定的抵抗粘附和變形。

          電極頭部的形狀對(duì)焊接粘附的影響,在點(diǎn)焊0.9mm的單面鍍鋅鋼板時(shí)間,用球面帽式電極焊3000點(diǎn)左右,已不能形成熔核,而用軸頸帽式電極,點(diǎn)焊8000點(diǎn)熔核直徑仍維持不變。

          文章來源:https://www.szagera.com/Article/dhjhjwsmhc.html

          近遇見一個(gè)吸頂效果的需求,要想實(shí)現(xiàn)這個(gè)效果,使用UI庫也可以,但如果使用position的sticky粘黏屬性可以輕易實(shí)現(xiàn),但在實(shí)際使用中需要注意一些細(xì)節(jié),于是寫了個(gè)簡單demo,效果如下

          <script src="https://lf6-cdn-tos.bytescm.com/obj/cdn-static-resource/tt_player/tt.player.js?v=20160723"></script>

          代碼如下

          <!DOCTYPE html>
          <html lang="en">
          <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>sticky</title>
            <style>
              html,body{
                height: 100%;
              }
              .wrap{
                overflow: auto;
                height: 100%;
              }
              .div1{
                height: 30px;
                background: red;
                text-align: center;
                line-height: 30px;
                color: aliceblue;
                position: sticky;
                top: 0;
              }
              .div2{
                height: 500px;
                background: #333;
                margin-bottom: 20px;
              }
            </style>
          </head>
          <body>
            <div class="wrap">
              <div class="div1">A</div>
              <div class="div2"></div>
              <div class="div1">B</div>
              <div class="div2"></div>
              <div class="div1">C</div>
              <div class="div2"></div>
              <div class="div1">D</div>
              <div class="div2"></div>
            </div>
          </body>
          </html>

          ps:

          • sticky 元素會(huì)“固定”在離它最近的一個(gè)擁有“滾動(dòng)機(jī)制”的祖先上
          • sticky會(huì)創(chuàng)建一個(gè)新的BFC(塊級(jí)格式化上下文)
          • 須指定 top, right, bottomleft 四個(gè)閾值其中之一,才可使粘性定位生效。否則其行為與相對(duì)定位相同,一般設(shè)置top:0

          主站蜘蛛池模板: 99热门精品一区二区三区无码| 国产suv精品一区二区33| 国模大胆一区二区三区| 97精品国产一区二区三区| 亚洲高清一区二区三区| 日本一区二区三区在线看| 亚洲午夜精品第一区二区8050| 狠狠做深爱婷婷综合一区| 国产AV国片精品一区二区| 国产成人精品一区二区三区免费| 91午夜精品亚洲一区二区三区| 冲田杏梨AV一区二区三区| 久久影院亚洲一区| 亚洲高清日韩精品第一区| 免费看AV毛片一区二区三区| 精品福利一区二区三区| 2021国产精品视频一区| 一区二区三区在线观看中文字幕 | 国产精品亚洲一区二区三区久久| 日韩一区二区在线观看| 亚洲午夜精品一区二区| 精彩视频一区二区三区| 亚洲AV无码一区二区二三区入口| 成人毛片一区二区| 久久青草国产精品一区| 精品国产一区二区三区久| 日韩电影一区二区三区| 国产成人无码精品一区不卡| 日本一区二区在线播放| 日本夜爽爽一区二区三区| 无码国产精品一区二区免费虚拟VR| 一区二区在线播放视频| 亚洲av日韩综合一区久热| 亚洲色大成网站www永久一区| 人妻精品无码一区二区三区| 无码成人一区二区| V一区无码内射国产| 夜精品a一区二区三区| 亚洲美女一区二区三区| 亚洲综合国产一区二区三区| 免费在线视频一区|