第三模块: (2)网络编程基础

计算机基础

应用 -> 控制程序(操作系统) -> 计算机硬件
c/s架构, 通过网络进行通讯。

什么是网络?

OSI七层协议
应用层 表示层 会话层 传输层 传输层 网络层 数据链路层 物理层

五层协议

  • 物理层 电信号
  • 数据链路层
    • 以太网协议(Ethernet)
    • 数据报或数据帧head+data
    • head18个字节(6源地址 6目的地址 6描述data类型)
    • 基于mac地址广播方式, 同一子网内通讯
  • 网络层
    • ip协议
    • ip头+data
    • arp协议: 将ip地址转换为mac地址
  • 传输层
    • tcp/udp
  • 应用层
    • http, ftp

1. 什么是c/s架构?

c/s又称Client/Server服务模式。这种结构的系统把较为复杂的计算和管理任务交给网络上的高档机器,而把频繁与客户打交道的任务交给客户机。任何一个应用系统,不管是简单的单机系统还是复杂的网络系统,都由3个部分组成:显示逻辑部分(表示层)、事务处理逻辑部分(功能层)和数据处理逻辑部分(数据层)。显示逻辑部分的功能是与用户进行交互;事务处理逻辑部分的功能是进行具体的运算和数据的处理;数据处理逻辑部分的功能是对数据库中的数据进行查询、修改和更新等。在两层模式的Client/Server结构中,显示逻辑部分和事务处理逻辑部分均被放在客户端,数据处理逻辑部分和数据库被放在服务器端。这样就使得客户端变得很“胖”,成为胖客户机,而服务器端的任务相对较轻,成为瘦服务器。

2. 互联网协议是什么?分别介绍五层协议中每一层的功能?

互联网协议是网络通信中统一的标准。
五层协议

  • 物理层 主要功能是基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0;

  • 数据链路层 主要功能定义了电信号的分组方式。关键字:ethernet,mac, 广播

    以太网协议-MAC地址封装.png

  • 网络层 功能是引入一套新的地址来区分不同的广播域、子网,这套地址即网络地址。关键字:ip协议、子网掩码、ip地址分类、ip报文、arp协议、ICMP、ping、tracetroute

    ip.png

  • 传输层 功能是建立端口到端口的通信,端口范围是0-65535, 0-1023为系统占用端口。传输层有两层层协议:TCP/UDP

    • tcp协议 可靠传输,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不必再分割。


      三次握手四次断开.png
    • udp协议, 不可靠传输,”报头”部分一共只有8个字节,总长度不超过65,535字节,正好放进一个IP数据包。
  • 应用层

3.基于tcp协议通信,为何建立链接需要三次握手,而断开链接却需要四次挥手?

三次握手

  1. 客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认。
  2. 服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态。
  3. 客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=y+1),此包发送完毕,服务端和客户端进入ESTABLISHED状态,完成三次握手。

四次挥手

  1. 主动关闭方发送一个FIN,来关闭到被动关闭方的数据发送。
  2. 被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1
  3. 被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,就是告诉主动关闭方,我的数据也发完了
  4. 主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序列号为收到序号+1

4.为何基于tcp协议的通信比基于udp协议的通信更可靠?

tcp:可靠 对方给了确认收到信息,才发下一个,如果没收到确认信息就重发
udp:不可靠 一直发数据,不需要对方回应

5.流式协议指的是什么协议,数据报协议指的是什么协议?

流式协议:TCP协议,可靠传输
数据报协议: UDP协议,不可传输

6.什么是socket?简述基于tcp协议的套接字通信流程

socket是应用层与TCP/IP协议族通信的中间软件抽象层,他是一组接口。


socket通信流程.png

7.什么是粘包? socket 中造成粘包的原因是什么? 哪些情况会发生粘包现象?

粘包:数据粘在一起,主要因为:接收方不知道消息之间的界限,不知道一次性提取多少字节的数据造成的
数据量比较小,时间间隔比较短,就合并成了一个包

8.基于socket开发一个聊天程序,实现两端互相发送和接收消息

#!/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,开发简单的远程命令执行程序,允许用户执行命令,并返回结果

#!/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协议编写简单FTP程序,实现上传、下载文件功能,并解决粘包问题

#!/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协议编写程序,实现功能

  • 执行指定的命令,让客户端可以查看服务端的时间
  • 执行指定的命令,让客户端可以与服务的的时间同步
# 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))  # 设置日期
    os.system("time %d:%d:%d.0" % (localtime.tm_hour, localtime.tm_min, localtime.tm_sec))  # 设置时间
 
client.close()

推荐阅读更多精彩内容