1. 程式人生 > >網路程式設計知識點剖析

網路程式設計知識點剖析

網路程式設計知識點剖析

. C/S 架構: Client / Server  客戶端 / 服務端

    B/S 架構: Browser / Server  前端 / 服務端

 

.網路程式設計通訊流程.

  網絡卡   mac地址   IP地址   子網掩碼   閘道器   DNS伺服器  (進行域名(domain name)和與之相對應的IP地址 (IP address)轉換的伺服器。)  

  DHCP (自動分配IP)    NAT (Network Address Translation,網路地址轉換)  埠   路由器

     交換機   集線器   廣播 單播 廣播風暴   arp協議(地址解析協議)    路由協議

 

.網路通訊協議

 1. osi七層:            tcp\ ip 五層:

      應用層              應用層  

      表示層              傳輸層

      會話層              網路層

      傳輸層              資料鏈路層

      網路層              物理層

      資料鏈路層

      物理層

  2. TCP\IP 協議存在 傳輸層   

    Tcp   : 面向連線,訊息可靠,效率相對差,面向流的訊息格式,無訊息保護邊界    

    Udp  : 面向無連線,訊息不可靠,效率高,面向包的訊息格式,有訊息保護邊界

    tcp三次握手:1.(client) > [SYN包 ]->(server)   請求建立連線

          2.(client) < [SYN/ACK] < (server)  severs收到syn 傳送[SYN/ACK]確認包

          3.(client)> [ACK] >(server)  client收到[SYN/ACK] 在發一個[ACK]確認包

    tcp四次揮手:1.(client) > [ACK/FIN] >(server) 傳送包 請求關閉

          2.(client) < [ACK] <(server)  收到包  同意關閉

          3.(client) <[ACK/FIN] <(server) 收到包   是否收到 同意關閉 訊息 

          4.(client) > [ACK/FIN] >(server)  傳送包  收到過程2的訊息,

四.socket

  1. 服務端與客戶端 手法訊息 基本結構

server>>>>>>>>>>>>>>>>>

import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8898))  #把地址繫結到套接字
sk.listen()          #監聽連結
conn,addr = sk.accept() #接受客戶端連結
ret = conn.recv(1024)  #接收客戶端資訊
print(ret)       #列印客戶端資訊
conn.send(b'hi')        #向客戶端傳送資訊
conn.close()       #關閉客戶端套接字
sk.close()        #關閉伺服器套接字(可選)

client>>>>>>>>>>>>>>>>
import socket
sk = socket.socket()           # 建立客戶套接字
sk.connect(('127.0.0.1',8898))    # 嘗試連線伺服器
sk.send(b'hello!')
ret = sk.recv(1024)         # 對話(傳送/接收)
print(ret)
sk.close()            # 關閉客戶套接字
tcp 服務端和客戶端通訊 基本程式碼

 

server>>>>>>>>>>>>>>>>>>>>>>>>

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #建立一個伺服器的套接字
udp_sk.bind(('127.0.0.1',9000))        #繫結伺服器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr)                 # 對話(接收與傳送)
udp_sk.close()                         # 關閉伺服器套接字

client>>>>>>>>>>>>>>>>>>>>>>>>

import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)
udp

  2.緩衝區

     作用:  提高效率,將傳送或者接受資訊的過程與網路傳輸隔離開,解耦

    特性:  1.I/O緩衝區在每個TCP套接字中單獨存在;
         2.I/O緩衝區在建立套接字時自動生成;
         3.即使關閉套接字也會繼續傳送輸出緩衝區中遺留的資料;
         4.關閉套接字將丟失輸入緩衝區中的資料。
    大小:  一般預設為 8K, 可以通過 getsockopt() 函式獲取

  3.粘包

    TCP會粘包、UDP永遠不會粘包

    粘包的兩種現象: 

    1.連續傳送小包並且間隔時間很短,就會發送兩個訊息合併在一起的情況,被一次接受了,(Nagel 優化演算法導致的,            避免連續傳送小包,影響傳輸xiaol)

    2.一次傳送資料過大,對方一次未接受完導致下次接受的時候,連同第一次剩下的訊息,一同接受了,導致粘包.

    解決粘包的方案: 

      原因是因為雙方不知道對方傳送的訊息長度

    1.傳送訊息之前先發送訊息的長度,然後收到對方的確認資訊後再發送訊息.

    2.通過struct模組,自定義包頭,將訊息長度打包成4個位元組長度的資訊,連同你要傳送的資料,一起傳送過去

      打包:pack('i',長度) 長度是個整數

      解包:unpack('i',接受的那4個位元組),得到的是元祖

    Sendall(): 迴圈的send直到資料傳送完畢.

    Socketsserver(): 實現tcp協議下,一個服務端可以同時和多個客戶端進行通訊.

 

import socket,struct,json
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

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()

        headers={'data_size':len(back_msg)}
        head_json=json.dumps(headers)
        head_json_bytes=bytes(head_json,encoding='utf-8')

        conn.send(struct.pack('i',len(head_json_bytes))) #先發報頭的長度
        conn.send(head_json_bytes) #再發報頭
        conn.sendall(back_msg) #在發真實的內容

    conn.close()
解決粘包方案一 server
import socket
import struct,json
client= socket.socket()
client.connect(("127.0.0.1",8080))

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_data
        recv_size = len(recv_data) + recv_size

    print(recv_data.decode('gbk'))
解決粘包解決方案一 client

 

import socketserver
class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print("{} wrote:".format(self.client_address[0]))
        print(self.data)
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "127.0.0.1", 9999

    # 設定allow_reuse_address允許伺服器重用地址
    socketserver.TCPServer.allow_reuse_address = True
    # 建立一個server, 將服務地址繫結到127.0.0.1:9999
    #server = socketserver.TCPServer((HOST, PORT),Myserver)
    server = socketserver.ThreadingTCPServer((HOST, PORT),Myserver)
    # 讓server永遠執行下去,除非強制停止程式
    server.serve_forever()
tcp_server_sockserver
import socket

HOST, PORT = "127.0.0.1", 9999
data = "hello"

# 建立一個socket連結,SOCK_STREAM代表使用TCP協議
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.connect((HOST, PORT))          # 連結到客戶端
    sock.sendall(bytes(data + "\n", "utf-8")) # 向服務端傳送資料
    received = str(sock.recv(1024), "utf-8")# 從服務端接收資料

print("Sent:     {}".format(data))
print("Received: {}".format(received))
tcp_client_socketserver