网络编程

一.网络基础相关知识:

  (1) 架构:

    a. C/S架构:Client客户端Sever服务端

      优势:能充分发挥PC的性能

    b.B/S架构:brower浏览器和serve服务器,隶属于C/S架构

      Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需

    在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。

      优势:同一了应用的接口

   (2)通信:

    a 同一台电脑上两个py程序通信:打开一个文件

    b.两个电脑如何通信:连一个网线

    c.多个电脑通信:

 ex : 电脑1(源)要找电脑2(目标)
  电脑1首先发送一个请求帧,期中包含(我的ip是192.168.1.1,我的mac地址是xxxxxxxx,我要找ip地址为192.168.1.2的主机),将此请求发送给交换机.
  交换机要广播这条消息给其他所有的主机
  目标主机接收到消息后,对比发现自己就是被找的主机,回复给交换机信息(我的ip地址是192.168.1.2,我的mac地址是yyyyyyyyy,请回复给ip地址为192.168.1.1,mac地址为xxxxxxx的主机)
  交换机单播形式返回给源主机
View Code

    (3)ip地址(标识了计算机在网络中的位置):

IP地址是指互联网协议地址(英语:Internet Protocol Address,又译为网际协议地址),是IP Address的缩写。
IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。 IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”(也就是4个字节)。
IP地址通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0
~255之间的十进制整数。例:
点分十进IP地址(100.4.5.6),实际上是32位二进制数(01100100.00000100.00000101.00000110)。

    小知识点:127.0.0.1 是个回环地址,每个计算机都有这么一个本机地址,只能被本机识别,不会被其他机器识别

    (4)端口:端口是英文port的意译,可以认为是设备与外界通讯交流的出口(操作系统为本机上每一个运行的程序分配一个端口,其他电脑上这个程序可以通过端口获取这个程序)

      ip地址 + 端口 能找到某台电脑上的某一个服务程序

    (5)mac地址:是一个物理地址,全球唯一,类似于身份证

    ps:mac地址是某台电脑特有的标志,ip地址是精确到具体的一台电脑,端口精确到具体的程序

    (6) arp协议:通过目标的ip地址获取目标的mac地址的一个协议

    (7)网关:类似于一个局域网的入口和出口

    (8)网段:一个局域网内的ip地址范围

    (9)路由器:连接不同的网段,路由

网络编程

    (10).子网掩码:子网掩码 & ip地址 得到网段

二.osi模型:人们按照分工不同把互联网协议从逻辑上划分了层级:

网络编程

每层相关的物理设备:

网络编程

 三.socket模块:(也叫套接字)

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,
它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。 其实站在你的角度上看,socket就是一个模块。我们通过调用模块中已经实现的方法建立两个进程之间的连接和通信。 也有人将socket说成ip+port,因为ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序。 所以我们只要确立了ip和port就能找到一个应用程序,并且使用socket模块来与之通信。

套接字类型:

1.基于文件类型的套接字类型

套接字家族的名字:AF_UNIX

unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信

2.基于网络类型的套接字家族

套接字家族的名字:AF_INET

(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我们只使用AF_INET)

 3.tcp协议和udp协议

TCP(Transmission Control Protocol):可靠的、面向连接的协议(eg:打电话)、传输效率低全双工通信(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;电子邮件、文件传输程序。

UDP(User Datagram Protocol):不可靠的、无连接的服务,传输效率高(发送前时延小),传送速度快,一对一、一对多、多对一、多对多、面向报文,尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。

网络编程

 四.套接字(socket)的使用

1.基于tcp协议的socket

tcp是基于链接的,必须先启动服务器,然后启动客户端去链接服务器

server端

import socket
sk = socket.socket() #创建一个套接字
sk.bind(('196.168.12.7',9000)) #把地址绑定到套接字
sk.listen()  #监听链接
conn,addr = sk.accept() #接收客户端链接
ret = conn.recv(1024) #接收客户端信息
print(ret.decode('utf-8')) #打印客户端信息
conn.send('你好'.encode('utf-8')) #向客户端发送信息
conn.close #关闭客户端套接字
sk.close #关闭服务端套接字(可选)

client端

import socket
sk = socket.socket() #创建客户套接字
sk.connect(('192.168.12.22',9000)) #尝试链接服务器
sk.send('你好'.encode('utf-8')) #发送信息
ret = sk.recv(1024) #对话(发送接收)
print(ret.decode('utf-8')) 
sk.close #关闭客户套接字

重启服务端是可能会遇到的问题:

网络编程

解决方法:

加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socker()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
sk.bind(('127.0.0.1',8000))
sk.listen()
conn,addr = sk.accept()
ret = conn.recv(1024)
print(ret.decode('utf-8')
conn.send('你好'.encode('utf-8'))
conn.close()
sk.colse()

tcp实现的一个聊天室

tcp三次握手:一定是client先发起请求:

  a.客户端发起请求连接服务器

  b.服务器返回:接收到请求,并要求连接客户端

  c.客户端回复:可以连接

   四次挥手:谁先发起断开连接的请求都可以

  a.客户端先发起断开连接的请求:

   意思是:我想和你断开连接,我没有数据要继续发送了,但是如果你有数据需要发送,我可以继收  

  b.服务端回复:我接收到你的请求了

  c.服务端发送:我已经准备好断开连接了

  d.客户端回复:收到你的信息,断开连接

基于UDP协议的socket

udp是无链接的,启动服务之后可以直接接收消息,不需要提前建立链接

udp协议的通信优势:允许一个服务器同事和多个客户端通信,tcp步行

server端:

import socket
sk = socket.socket(type = socket.SOCK_DGRAM) #创建一个服务器端套接字
sk.bind(('192.168.1.1',9000)) #绑定服务器套接字
mag,addr = sk.resvfrom(1024)
print(mag.decode('utf-8'))
sk.sendto('你好',addr)  #对话
sk.close  #关闭服务器套接字

client端:

import socket
ip_port = ('192.168.1.1')
sk = socket.socket(type = socket.SOCK_DGRAM)
sk.sendto('你好'.encode('utf-8'))
back_mag,addr = sk.resvfrom(1024)
print(back_mag.decode('utf-8'),addr)

socket参数详解

socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)

 

family 地址系列应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。
(AF_UNIX 域实际上是使用本地 socket 文件来通信)
type 套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。
SOCK_STREAM 是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送。 
SOCK_DGRAM 是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播信息。
proto 协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。
fileno 如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。
与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的。
这可能有助于使用socket.close()关闭一个独立的插座

实现一个省去写编码解码的类,这个类继承socket