day8--socket網絡編程進階

    socket:socket就是實現服務器和客戶端數據的交換,服務器端接收併發送數據,客戶端發送並接收數據,而且須要注意的是,在python3中,socket值接收字節。由於客戶端在發送鏈接給服務器的時候,要轉換爲字節碼;服務器端在返回給客戶端的時候,也要轉換爲字節碼。python

    以下所示:編程

服務器端:緩存

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

    首先啓動服務器端,而後啓動客戶端,以下所示:併發

    服務器端發送數據:socket

>>: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
上節內容

    客戶端接收數據:ui

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

    當客戶端卡頓的時候,說明服務器端是沒有數據發送過來的,由於客戶端不能接收空的數據服務器端也不能接收空的數據。這樣就會形成卡頓的狀況。spa

    在接收的時候,會有一個緩存,緩存不滿的時候,是不會發送數據的,客戶端就接收不到數據。code

    因爲緩存的存在,客戶端接收數據有時候接收不完整,如何避免呢?要求服務器告訴客戶端數據的大小,客戶端根據數據的大小來接收數據。

    收發同樣大,告訴客戶端接收數據的大小,以下:

服務器端:

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()執行,只有第一次發送成功以後,而且經過驗證,再來進行第二次執行。

相關文章
相關標籤/搜索