第三模塊: (2)網(wǎng)絡(luò)編程基礎(chǔ)

計算機基礎(chǔ)

應(yīng)用 -> 控制程序(操作系統(tǒng)) -> 計算機硬件
c/s架構(gòu), 通過網(wǎng)絡(luò)進行通訊。

什么是網(wǎng)絡(luò)?

OSI七層協(xié)議
應(yīng)用層 表示層 會話層 傳輸層 傳輸層 網(wǎng)絡(luò)層 數(shù)據(jù)鏈路層 物理層

五層協(xié)議

  • 物理層 電信號
  • 數(shù)據(jù)鏈路層
    • 以太網(wǎng)協(xié)議(Ethernet)
    • 數(shù)據(jù)報或數(shù)據(jù)幀head+data
    • head18個字節(jié)(6源地址 6目的地址 6描述data類型)
    • 基于mac地址廣播方式, 同一子網(wǎng)內(nèi)通訊
  • 網(wǎng)絡(luò)層
    • ip協(xié)議
    • ip頭+data
    • arp協(xié)議: 將ip地址轉(zhuǎn)換為mac地址
  • 傳輸層
    • tcp/udp
  • 應(yīng)用層
    • http, ftp

1. 什么是c/s架構(gòu)?

c/s又稱Client/Server服務(wù)模式。這種結(jié)構(gòu)的系統(tǒng)把較為復(fù)雜的計算和管理任務(wù)交給網(wǎng)絡(luò)上的高檔機器,而把頻繁與客戶打交道的任務(wù)交給客戶機。任何一個應(yīng)用系統(tǒng),不管是簡單的單機系統(tǒng)還是復(fù)雜的網(wǎng)絡(luò)系統(tǒng),都由3個部分組成:顯示邏輯部分(表示層)、事務(wù)處理邏輯部分(功能層)和數(shù)據(jù)處理邏輯部分(數(shù)據(jù)層)。顯示邏輯部分的功能是與用戶進行交互;事務(wù)處理邏輯部分的功能是進行具體的運算和數(shù)據(jù)的處理;數(shù)據(jù)處理邏輯部分的功能是對數(shù)據(jù)庫中的數(shù)據(jù)進行查詢、修改和更新等。在兩層模式的Client/Server結(jié)構(gòu)中,顯示邏輯部分和事務(wù)處理邏輯部分均被放在客戶端,數(shù)據(jù)處理邏輯部分和數(shù)據(jù)庫被放在服務(wù)器端。這樣就使得客戶端變得很“胖”,成為胖客戶機,而服務(wù)器端的任務(wù)相對較輕,成為瘦服務(wù)器。

2. 互聯(lián)網(wǎng)協(xié)議是什么?分別介紹五層協(xié)議中每一層的功能?

互聯(lián)網(wǎng)協(xié)議是網(wǎng)絡(luò)通信中統(tǒng)一的標準。
五層協(xié)議

  • 物理層 主要功能是基于電器特性發(fā)送高低電壓(電信號),高電壓對應(yīng)數(shù)字1,低電壓對應(yīng)數(shù)字0;

  • 數(shù)據(jù)鏈路層 主要功能定義了電信號的分組方式。關(guān)鍵字:ethernet,mac, 廣播

    以太網(wǎng)協(xié)議-MAC地址封裝.png

  • 網(wǎng)絡(luò)層 功能是引入一套新的地址來區(qū)分不同的廣播域、子網(wǎng),這套地址即網(wǎng)絡(luò)地址。關(guān)鍵字:ip協(xié)議、子網(wǎng)掩碼、ip地址分類、ip報文、arp協(xié)議、ICMP、ping、tracetroute

    ip.png

  • 傳輸層 功能是建立端口到端口的通信,端口范圍是0-65535, 0-1023為系統(tǒng)占用端口。傳輸層有兩層層協(xié)議:TCP/UDP

    • tcp協(xié)議 可靠傳輸,TCP數(shù)據(jù)包沒有長度限制,理論上可以無限長,但是為了保證網(wǎng)絡(luò)的效率,通常TCP數(shù)據(jù)包的長度不會超過IP數(shù)據(jù)包的長度,以確保單個TCP數(shù)據(jù)包不必再分割。


      三次握手四次斷開.png
    • udp協(xié)議, 不可靠傳輸,”報頭”部分一共只有8個字節(jié),總長度不超過65,535字節(jié),正好放進一個IP數(shù)據(jù)包。
  • 應(yīng)用層

3.基于tcp協(xié)議通信,為何建立鏈接需要三次握手,而斷開鏈接卻需要四次揮手?

三次握手

  1. 客戶端發(fā)送syn包(syn=x)到服務(wù)器,并進入SYN_SEND狀態(tài),等待服務(wù)器確認。
  2. 服務(wù)器收到syn包,必須確認客戶的SYN(ack=x+1),同時自己也發(fā)送一個SYN包(syn=y),即SYN+ACK包,此時服務(wù)器進入SYN_RECV狀態(tài)。
  3. 客戶端收到服務(wù)器的SYN+ACK包,向服務(wù)器發(fā)送確認包ACK(ACK=y+1),此包發(fā)送完畢,服務(wù)端和客戶端進入ESTABLISHED狀態(tài),完成三次握手。

四次揮手

  1. 主動關(guān)閉方發(fā)送一個FIN,來關(guān)閉到被動關(guān)閉方的數(shù)據(jù)發(fā)送。
  2. 被動關(guān)閉方收到FIN包后,發(fā)送一個ACK給對方,確認序號為收到序號+1
  3. 被動關(guān)閉方發(fā)送一個FIN,用來關(guān)閉被動關(guān)閉方到主動關(guān)閉方的數(shù)據(jù)傳送,就是告訴主動關(guān)閉方,我的數(shù)據(jù)也發(fā)完了
  4. 主動關(guān)閉方收到FIN后,發(fā)送一個ACK給被動關(guān)閉方,確認序列號為收到序號+1

4.為何基于tcp協(xié)議的通信比基于udp協(xié)議的通信更可靠?

tcp:可靠 對方給了確認收到信息,才發(fā)下一個,如果沒收到確認信息就重發(fā)
udp:不可靠 一直發(fā)數(shù)據(jù),不需要對方回應(yīng)

5.流式協(xié)議指的是什么協(xié)議,數(shù)據(jù)報協(xié)議指的是什么協(xié)議?

流式協(xié)議:TCP協(xié)議,可靠傳輸
數(shù)據(jù)報協(xié)議: UDP協(xié)議,不可傳輸

6.什么是socket?簡述基于tcp協(xié)議的套接字通信流程

socket是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層,他是一組接口。


socket通信流程.png

7.什么是粘包? socket 中造成粘包的原因是什么? 哪些情況會發(fā)生粘包現(xiàn)象?

粘包:數(shù)據(jù)粘在一起,主要因為:接收方不知道消息之間的界限,不知道一次性提取多少字節(jié)的數(shù)據(jù)造成的
數(shù)據(jù)量比較小,時間間隔比較短,就合并成了一個包

8.基于socket開發(fā)一個聊天程序,實現(xiàn)兩端互相發(fā)送和接收消息

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-5-7 下午8:27
# @File : sever.py

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', 8080))
server.listen(5)

print('Starting...')

while True:
    conn, addr = server.accept()
    while True:
        data = conn.recv(1024)
        if not data:
            break
        print("%s: %s" % (addr[0], data.decode('utf-8')))
        msg = input(">>>:").strip()
        if not msg:
            continue
        conn.send(msg.encode('utf-8'))
    conn.close()
server.close()

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-5-7 下午8:28
# @File : client.py

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

client.connect(('127.0.0.1', 8080))

while True:
    msg = input(">>>: ").strip()
    if not msg:
        continue
    client.send(msg.encode('utf-8'))
    data = client.recv(1024)
    print(data.decode('utf-8'))

9.基于tcp socket,開發(fā)簡單的遠程命令執(zhí)行程序,允許用戶執(zhí)行命令,并返回結(jié)果

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-5-7 下午8:27
# @File : sever.py

import socket
import subprocess

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', 8080))
server.listen(5)

print('Staring...')

while True:
    conn, addr = server.accept()
    while True:
        data = conn.recv(1024)
        if not data:
            break
        obj = subprocess.Popen(data, shell=True, stdout=subprocess.PIPE, stderr= subprocess.PIPE)
        conn.send(obj.stdout.read())
        conn.send(obj.stderr.read())
    conn.close()
server.close()

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-5-7 下午8:28
# @File : client.py

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

client.connect(('127.0.0.1', 8080))

while True:
    msg = input(">>>: ").strip()
    if not msg:
        continue
    client.send(msg.encode('utf-8'))
    data = client.recv(1024)
    print(data.decode('utf-8'))

10. 基于tcp協(xié)議編寫簡單FTP程序,實現(xiàn)上傳、下載文件功能,并解決粘包問題

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-5-7 下午8:27
# @File : sever.py

import struct
import json
import socket
from os import path

file_dir = path.join(path.dirname(__file__), 'data')

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', 8080))
server.listen(5)

print('Staring...')

def pack_header(data, filename):
    header = {
        'file_name': path.split(filename)[1],
        'data_size': len(data),
        'exists': path.isfile(filename),
    }
    json_header = json.dumps(header).encode('utf-8')
    header_len = struct.pack('i', len(json_header))
    return header_len, json_header


while True:
    conn, addr = server.accept()
    while True:
        cmd = conn.recv(1024)
        if not cmd:
            break
        command = cmd.decode('utf-8').split()
        if command[0] == 'get':
            file = command[1]
            filename = path.join(file_dir, file)
            if path.isfile(filename):
                with open(filename, 'rb') as f:
                    data= f.read()
            else:
                data = '%s not exists...' % file
                data = data.encode('utf-8')
            p_header = pack_header(data, filename)
            conn.send(p_header[0])
            conn.send(p_header[1])
            conn.send(data)
        elif command[0] == 'put':
            print(123)
            header_len = struct.unpack('i', conn.recv(4))[0]
            print(header_len)
            json_header = conn.recv(header_len)
            print(json_header)
            header = json.loads(json_header.decode('utf-8'))
            file = path.split(header['file_name'])[1]
            data_size = header['data_size']
            size = 0
            filename = path.join(file_dir, file)
            with open(filename, 'wb') as f:
                while size < data_size:
                    data = conn.recv(1024)
                    f.write(data)
                    size += len(data)
    conn.close()
server.close()

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-5-7 下午8:28
# @File : client.py

import struct
import json
import socket
from os import path

file_dir = path.join(path.dirname(__file__), 'data')

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

client.connect(('127.0.0.1', 8080))

def pack_header(data, filename):
    header = {
        'file_name': path.split(filename)[1],
        'data_size': len(data),
        'exists': path.isfile(filename),
    }
    json_header = json.dumps(header).encode('utf-8')
    header_len = struct.pack('i', len(json_header))
    return header_len, json_header


while True:
    msg = input(">>>: ").strip()
    if not msg:
        continue
    if msg.startswith('get'):
        client.send(msg.encode('utf-8'))
        header_len = struct.unpack('i', client.recv(4))[0]
        json_header = client.recv(header_len)
        header = json.loads(json_header.decode('utf-8'))
        file = path.split(header['file_name'])[1]
        data_size = header['data_size']
        size = 0
        filename = path.join(file_dir, file)
        if header['exists']:
            with open(filename, 'wb') as f:
                while size < data_size:
                    data = client.recv(1024)
                    f.write(data)
                    size += len(data)
        else:
            data = client.recv(1024).decode('utf-8')
            print(data)
    elif msg.startswith('put'):
        client.send(msg.encode('utf-8'))
        command = msg.split()
        file = command[1]
        filename = path.join(file_dir, file)
        if path.isfile(filename):
            with open(filename, 'rb') as f:
                data = f.read()
            p_header = pack_header(data, filename)
            client.send(p_header[0])
            client.send(p_header[1])
            client.send(data)
        else:
            print('%s not exists...' % file)

client.close()

11. 基于udp協(xié)議編寫程序,實現(xiàn)功能

  • 執(zhí)行指定的命令,讓客戶端可以查看服務(wù)端的時間
  • 執(zhí)行指定的命令,讓客戶端可以與服務(wù)的的時間同步
# sever.py
# _*_ coding: utf-8 _*_
import socket
import subprocess
import time
 
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(('127.0.0.1', 8080))
while True:
    data, client_addr = server.recvfrom(1024)
    print(data, client_addr)
    obj = subprocess.Popen(data.decode('utf-8'),shell=True,  # time 命令在windows 下不能用
                     stdout=subprocess.PIPE,
                     stderr=subprocess.PIPE)
    stdout = obj.stdout.read()
    stderr = obj.stderr.read()
    print(stdout+stderr)
    server.sendto(stdout+stderr,client_addr)
    if data.decode('utf-8') == 'time':
        str_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        # str_time = '2017-01-01 00:00:00'
        server.sendto(str_time.encode('gbk'), client_addr)
 
server.close()

# client.py
# _*_ coding: utf-8 _*_
import socket
import os
import time
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
    msg = input('>>>:').strip()
    client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
    data,server_addr = client.recvfrom(1024)
    print(data.decode('utf-8'),server_addr)
    localtime = time.localtime()
    os.system("date %d-%d-%d" % (localtime.tm_year, localtime.tm_mon, localtime.tm_mday))  # 設(shè)置日期
    os.system("time %d:%d:%d.0" % (localtime.tm_hour, localtime.tm_min, localtime.tm_sec))  # 設(shè)置時間
 
client.close()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容