网络编程

网络编程

1. C/S B/S 架构

​ C : client 端 客户端

​ B : Browser , 浏览器

​ S : Server 服务器

C/S 架构 所有app都是C/S架构

​ 优点 : 稳定性高,响应速度快,安全性高,功能全面 , 差异化个性化显示

​ 缺点: 开发成本高,维护成本高,面向客户固定

B/S架构 浏览器 与服务器 之间的架构 特殊的 C/S架构

​ 优点 : 开发维护成本低 , 面向用户广泛

​ 缺点 : 安全性不高 , 响应速度慢 , 个性化设置单一

2. 互联网通信原理

​ 数据交换

​ 为了通信的广泛性 , 需要一个共通的协议

路由器

1. 虚拟分配ip地址:DHCP协议
  1. 网关: 连接外网,
  2. 计算最优路径

3.七层协议(五层协议)

  1. 物理层 : 物理传输介质 , 发送的是010101byte数据流 , 但不知道发送给谁,所以需要添加mac地址

  2. 数据链路层 : 根据以太网协议将数据进行分组 , 添加数据头, 每一组称为一帧

    1. head 数据头 18个字节 源地址6| 目标地址6| 数据类型6
    2. data 数据体 46个字节 < data < 1500字节

    理论上来讲 , 这样就可以通过广播和任意一台电脑通信了 , 但是并不是所有电脑都在一个子网 , 所以还需要知道与之通信的局域网的位置

  3. 网络层: IP协议: 确定对方局域网位置(也就是ip地址) , 最后再确定通信软件

  4. 传输层 : TCP UDP 端口协议 1--65535个端口 1 -- 1023 系统端口

  5. 应用层 : 软件自己的协议

mac 地址  厂商编号 + 流水线编号
IP地址  通过ARP协议--->> mac地址  (ip地址通过软件帮我们实现)例如 0.0.0.0 - 255.255.255.255  (八位 所以最大是255)
子网掩码: 255.255.255.0(c类)
网络层通信:
1 .ip地址和子网掩码找到局域网位置
2 .将 ip 和 子网掩码  转换为 二进制 然后取 and 运算  
3 . 如果不在同一个局域网 : 要通过网关-->路由协议-->通信

TCP

tcp好处: 安全 , 稳定

​ 缺点: 传输速度慢,效率低

TCP 三次握手 四次挥手
## 三次握手
1. client  syn = 1 seq = x  发送请求给  server
2. server  响应  ack = x+1 回复  client 
3. server syn = 1 seq = y  请求 client
4 client  ack = y+1  响应  server
2,3可以合成一步
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
--------------------- 

原文:https://blog.csdn.net/qq_38950316/article/details/81087809 
## 四次挥手
1 client fin = 1 , seq = u 发送终止请求 (不发数据了)  server  终止等待 
2 server 响应 ack =1 ,seq = v , ack = u+1  client (已确定客户端不会再发数据了 , 但是服务器想发数据客户端还是可以接到)  关闭等待
3 server 发送 fin = 1 ack = 1 seq = w ack = u+1 最后确认
4 client 发送 ack = 1 , seq = u+1 , ack = w+1 (这时并没有断开连接,需要等server先释放连接)
【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

【问题3】为什么不能用两次握手进行连接?

答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

       现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
--------------------- 

原文:https://blog.csdn.net/qq_38950316/article/details/81087809 

UDP

效率高 , 速度快

socket 套接字(提供一套接口的关键字)

  1. 应用层与传输层之间的抽象层
  2. python模块
    1. 提升开发效率 , 不需要将过多的时间花在计算机底层实现上
模拟网络通信
server
# server
import socket
## 实例化对象  参数默认设置为TCP
server = socket.socket()
# 绑定ip地址 和端口号
server.bind(('127.0.0.1',8900))
# 开启监听
server.listen(5)
# 同意 并 开通管道
conn , addr = server.accept()
while 1:
	## 接收内容
    client_data = conn.recv(1024)
    if client_data.decode()=='q':
        break
    print(f'男神:{client_data.decode()}')

    data = input('>>>')
 	## 回复内容
 	conn.send(data.encode())

conn.close()

import socket

client = socket.socket()
client.connect(('127.0.0.1',8900))

while True:

    s = input('>>>')
    if s == 'q':
        client.send('q'.encode())
        break
    client.send(s.encode())
    server_data = client.recv(1024)
    print(server_data.decode())
client.close()
import socket
import subprocess
# server
server = socket.socket()

server.bind(('127.0.0.1',8900))

server.listen(5)
while 1:
    conn , addr = server.accept()
    while 1:
        try:
            client_data = conn.recv(1024)
            if client_data.decode() == 'q':
                break
            ret = subprocess.Popen(client_data.decode(),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            true_msg = ret.stdout.read()
            err_msg = ret.stderr.read()
            conn.send(true_msg+err_msg)
        except:
            break
# client

import socket

client = socket.socket()
## 请求连接
client.connect(('127.0.0.1',8900))
while 1:
    data = input('>>>')
    # 发送数据
    client.send(data.encode())
    if data.upper()=='Q':
        break
    # 接收服务器返回数据
    server_data = client.recv(1024)

    print(f"女神:{server_data.decode('gbk')}")

client.close()

数据缓冲区

保持稳定性 , 减少与磁盘的交互 , 提高上传下载的效率

为防止网络异常或波动,导致的数据中断

粘包现象

产生原因 : 对方不知道数据大小 , 不知道界限, 不知道取多少

数据的传输过程 : client端 send ---> 同时 resv 在缓冲区接收从server端传来的数据 , 有就收取 , 因为每次resv字节有限 , 所以如果server端一次传输的数据长度大于resv一次读取的数据长度 , 那么 缓存区就会有遗留 , 那么下一次再send时,因为缓冲区有数据 , 那么recv就会直接接收 , 这就会出现问题

QQ图片20190717160945

解决

## 根据粘包生成原因 , 只要我让接收方 知道文件大小, 知道界限 , 不就可以了吗?
# server 端
import subprocess
import socket
import json
import struct
import os

server = socket.socket()

server.bind(('127.0.0.1',9000))

server.listen(5)

conn , addr = server.accept()

file_name = conn.recv(1024)

ret = subprocess.Popen(file_name.decode(),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)

stderr_data = ret.stderr.read()
stdout_data = ret.stdout.read()
data = stdout_data+stderr_data

dict = {
    'file_name':'file',
    'size':os.path.getsize(file_name)
}

json_dic = json.dumps(dict).encode()

json_dic_size = len(json_dic)

total_byte = struct.pack('i',json_dic_size)

conn.send(total_byte)
conn.send(json_dic)
f = open(file_name,mode='rb')
while 1:
    content = f.read(1024)
    if content:
        conn.send(content)
        print(content)
    else:
        break

#client 端
import socket
import json
import struct
import time
import os
client = socket.socket()

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

while 1:
    file_name = input('请输入:')
    client.send(file_name.encode())

    total_byte = client.recv(4)
    total_int = struct.unpack('i',total_byte)[0]
    json_dict = client.recv(total_int)
    dict = json.loads(json_dict)
    s = b''
    f = open('new'+file_name,mode='wb')
    print(dict['size'])
    t1 = time.time()
    while len(s)<dict['size']:
        s += client.recv(2048)
        print(len(s)/dict['size'])
    f.write(s)
    print(time.time()-t1)
    f.close()



Title

回到顶部
posted on 2019-07-22 15:12  _albert  阅读(132)  评论(0编辑  收藏  举报