pythdon day13:网络编程socket
day 13 learning python
2019/09/10
49. 网络基础
49.1 ip地址
- ip地址
- 如何在网络中唯一标识一台计算机?>>>ip地址
- 同一台计算机上的多个程序如何共用网络而不冲突?>>>网络端口
- 不同的计算机通信怎么才能互相理解?>>>使用相同的协议
ip地址:用来在网络中标记一台电脑的一串数字,比如192.168.1.1(c类);在同一网络上是唯一的(用来标记唯一的一台电脑)。
每一个ip地址包括两部分:网络地址和主机地址。前三位是网络号,最后是主机号。
主机号0,255两个数不能使用(网络号/广播地址)
a类ip地址由1字节的⽹络地址和3字节主机地址组成, ⽹络地址的最⾼位必须是“0”,地址范围1.0.0.1-126.255.255.254可⽤的a类⽹络有126个, 每个⽹络能容纳1677214个主机。
b类ip地址由2个字节的⽹络地址和2个字节的主机地址组成, ⽹络地址的最⾼位必须是“10”,地址范围128.1.0.1-191.255.255.254 可⽤的b类⽹络有16384个, 每个⽹络能容纳65534主机。
c类ip地址由3字节的⽹络地址和1字节的主机地址组成, ⽹络地址的最⾼位必须是“110”范围192.0.1.1-223.255.255.254 c类⽹络可达2097152个, 每个⽹络能容纳254个主机。
d类ip地址第⼀个字节以“1110”开始, 它是⼀个专一保留的地址。它并不指向特定的⽹络, ⽬前这⼀类地址被⽤在多点⼴播(一对多) 中多点⼴播地址⽤来⼀次寻址⼀组计算机 地址范围224.0.0.1-239.255.255.254。
e类ip地址以“1111”开始, 为将来使⽤保留 e类地址保留, 仅作实验和开发⽤。
私有ip:本地局域网上的ip,专门为组织机构内部使用
在这么多⽹络ip中, 国际规定有⼀部分ip地址是⽤于我们的局域⽹使⽤, 属于私⽹ip, 不在公⽹中使⽤的, 它们的范围是:
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
私有ip:局域网通信,内部访问,不能在外网公用。私有ip禁止出现在internet中,来自于私有ip的流量全部都会阻止并丢掉
公有ip:全球访问
ip地址127. 0. 0. 1~127. 255. 255. 255⽤于回路测试
测试当前计算机的网络通信协议
如: 127.0.0.1可以代表本机ip地址, ⽤http://127.0.0.1
就可以测试本机中配置的web服务器
常用来ping 127.0.0.1来看本地ip/tcp正不正常,如能ping通即可正常使用
- 子网掩码
⼦⽹掩码:是我们测量两个ip是否属于同一个网段的工具。
⼦⽹掩码不能单独存在, 它必须结合ip地址⼀起使⽤。
⼦⽹掩码只有⼀个作⽤, 就是将某个ip地址划分成⽹络地址和主机地址两部分
⼦⽹掩码的设定必须遵循⼀定的规则:与ip地址相同, ⼦⽹掩码的长度也是32位,左边是⽹络位, ⽤⼆进制数字“1”表示;右边是主机位, ⽤⼆进制数字“0”表示。
假设ip地址为“192.168.1.1”⼦⽹掩码为“255.255.255.0”。其中, “1”有24个, 代表与此相对应的ip地址左边24位是⽹络号;“0”有8个, 代表与此相对应的ip地址右边8位是主机号。
- 网络端口
端口号: 用来标记区分进程。
⼀台拥有ip地址的主机可以提供许多服务, ⽐如http(万维⽹服务) 、 ftp(⽂件传输) 、 smtp(电⼦邮件) 等, 这些服务完全可以通过1个ip地址来实现。 那么, 主机是怎样区分不同的⽹络服务呢?
显然不能只靠ip地址, 因为ip地址与⽹络服务的关系是⼀对多的关系。实际上是通过“ip地址+端⼝号”来区分不同的服务的。
端⼝号是一个数字,只有整数, 范围是从0到65535 (分为知名和动态两种
知名端⼝是众所周知的端⼝号(用来做固定事情), 范围从0到1023
1. 80端分配给http服务(网站) 2. 21端分配给ftp服务(文件下载)
可以理解为, ⼀些常⽤的功能使⽤的号码是固定的。
动态端⼝的范围是从1024到65535之所以称为动态端⼝, 是因为它⼀般不固定分配某种服务, ⽽是动态分配。动态分配是指当⼀个系统进程或应⽤程序进程需要⽹络通信时, 它向主机申请⼀个端⼝, 主机从可⽤的端⼝号中分配⼀个供它使⽤。
49.2 协议
协议:约定好的规范.
早期的计算机⽹络, 都是由各⼚商⾃⼰规定⼀套协议, ibm、 apple和microsoft都有各⾃的⽹络协议, 互不兼容(语言、方言、阿帕网)。
为了把全世界的所有不同类型的计算机都连接起来, 就必须规定⼀套全球通⽤的协议, 为了实现互联⽹这个⽬标, 互联⽹协议簇(internet protocol suite) 就是通⽤协议标准。
因为互联⽹协议包含了上百种协议标准, 但是最重要的两个协议是tcp和ip协议, 所以, ⼤家把互联⽹的协议总称tcp/ip协议。(大家都遵循的最基本网络通信协议)
是完成进程之间通信的规范。
tcp/ip定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准 .
4层的层级结构中,每一层都呼叫它的下一层所提供的网络来完成自己的需求.
其中的应用层关注的是应用程序的细节,而不是数据在网络中的传输活动其他三层主要处理所有的通信细节,对应用程序一无所知;
应用层:应用程序间沟通的层,不同的文件系统有不同的文件命名原则和不同的文本行表示方法等,不同的系统之间传输文件还有各种不兼容问题,这些都将由应用层来处理
传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(tcp)、用户数据报协议(udp)等,这一层负责传送数据,并且确定数据已被送达并接收
网络层:负责提供基本的数据包传送功能,让每一块数据包都能够到达目的主机。网络层接收由更低层发来的数据包,并把该数据包发送到更高层,相反,ip层也把从tcp或udp层接收来的数据包传送到更低层
网络接口层(链路层):对实际的网络媒体的管理,定义如何使用实际网络来传送数据(处理机械的、电气的和过程的接口)
了解即可,面试经常会问网络协议分几层
50. socket编程(套接字编程)
50.1 socket编程简介
socket:通过网络完成进程间通信的方式(区别于一台计算机之间进程通信)。
socket的英文原谅是插孔,通常也称作套接字。
socket本质是编程接口(api): socket 是对 tcp/ip 协议的封装,socket 只是个编程接口不是协议,通过 socket 我们才能使用 tcp/ip 协议簇(程序员层面).
tcp/ip也要提供可供程序员做网络开发所用的接口,这就是socket编程接口; http是轿车,提供了封装或者显示数据的具体形式;socket是发动机,提供了网络通信的能力
最重要的是,socket是面向客户/服务器模型而设计的,针对客户和服务器程序提供不同的socket系统调用
套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认.
50.2 创建socket
socket类初始化时有4个参数
''' socket(family=af_inet, type=sock_stream, proto=0) -> socket object socket(family=-1, type=-1, proto=-1, fileno=none) -> socket object open a socket of the given type. the family argument specifies the address family; it defaults to af_inet. the type argument specifies whether this is a stream (sock_stream, this is the default) or datagram (sock_dgram) socket. the protocol argument defaults to 0, specifying the default protocol. keyword arguments are accepted. the socket is created as non-inheritable. when a fileno is passed in, family, type and proto are auto-detected, unless they are explicitly set. a socket object represents one endpoint of a network connection. methods of socket objects (keyword arguments not allowed): _accept() -- accept connection, returning new socket fd and client address bind(addr) -- bind the socket to a local address close() -- close the socket connect(addr) -- connect the socket to a remote address connect_ex(addr) -- connect, return an error code instead of an exception dup() -- return a new socket fd duplicated from fileno() fileno() -- return underlying file descriptor getpeername() -- return remote address [*] getsockname() -- return local address getsockopt(level, optname[, buflen]) -- get socket options gettimeout() -- return timeout or none listen([n]) -- start listening for incoming connections recv(buflen[, flags]) -- receive data recv_into(buffer[, nbytes[, flags]]) -- receive data (into a buffer) recvfrom(buflen[, flags]) -- receive data and sender's address recvfrom_into(buffer[, nbytes, [, flags]) -- receive data and sender's address (into a buffer) sendall(data[, flags]) -- send all data send(data[, flags]) -- send data, may not send all of it sendto(data[, flags], addr) -- send data to a given address setblocking(0 | 1) -- set or clear the blocking i/o flag getblocking() -- return true if socket is blocking, false if non-blocking setsockopt(level, optname, value[, optlen]) -- set socket options settimeout(none | float) -- set or clear the timeout shutdown(how) -- shut down traffic in one or both directions if_nameindex() -- return all network interface indices and names if_nametoindex(name) -- return the corresponding interface index if_indextoname(index) -- return the corresponding interface name [*] not available on all platforms! '''
- family参数:默认是af_inet(ipv4协议⽤于 internet 进程间通信).
- type参数:套接字类型, 默认是 sock_stream(流式套接字, ⽤于tcp 协议) 或者 sock_dgram(数据报套接字, ⽤于 udp 协议).
- proto参数:默认0,代表默认协议。
tcp慢但是稳定不会丢数据
udp快但是可能会丢数据(黑客攻击)
确定了ip地址端口号(ipv4协议),tcp或udp协议之后,计算机之间可以进行通信.
import socket # 导入套接字模块 s = socket.socket(socket.af_inet, socket.sock_stream) # s此时是一个socket对象,拥有发送和接收网络数据的功能
50.3 udp和tcp
udp --- user data protocol,用户数据报协议, 是⼀个⽆连接的简单的⾯向数据报的传输层协议。 udp不提供可靠性, 它只是把应⽤程序传给ip层的数据报发送出去, 但是并不能保证它们能到达⽬的地。 由于udp在传输数据报前不⽤在客户和服务器之间建⽴⼀个连接, 且没有超时重发等机制, 故⽽传输速度很快.
udp⼀般⽤于多点通信和实时的数据业务, ⽐如:
- 语⾳⼴播/视频/qq/tftp(简单⽂件传送)
- udp可以理解为写信.
tcp(transmission control protocol,传输控制协议)是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接.
一个tcp连接必须要经过三次“对话”才能建立起来,其中的过程非常复杂,只简单的描述下这三次对话的简单过程:
主机a向主机b发出连接请求数据包:“我想给你发数据,可以吗?”,这是第一次对话;
主机b向主机a发送同意连接和要求同步(同步就是两台主机一个在发送,一个在接收,协调工作)的数据包:“可以,你什么时候发?”,这是第二次对话;
主机a再发出一个数据包确认主机b的要求同步:“我现在就发,你接着吧!”,这是第三次对话.
三次“对话”的目的是使数据包的发送和接收同步,经过三次“对话”之后,主机a才向主机b正式发送数据
可以理解为打电话,先建立通道
- tcp和udp的区别
- 基于连接与无连接
- 对系统资源的要求(tcp较多,udp少)
- udp程序结构较简单
- 流模式与数据报模式
- tcp保证数据正确性,udp可能丢包,tcp保证数据顺序,udp不保证
50.4 udp编程
-
udp发送数据
# 为看到效果,需要先装网络调试助手netassist from socket import * #导入套接字模块的所有内容 s = socket(af_inet,sock_dgram) # 创建套接字 add = ('192.168.56.1',8081) # 准备接收方地址 str = input('请输入发送的内容》》》') s.sendto(str.encode(),add) #发送数据时,python3需要将字符串转换成byte # encode('utf-8) 表示用utf-8对数据进行编码,获得bytes类型对象 # decode()反过来 s.close
发送数据给飞秋
- 飞秋使用:2425端口
- 发送普通数据,飞秋不会响应,必须发送特殊格式的内容
- 1:123123:吴彦祖:吴彦祖-pc:32:haha
- 飞秋有自己的应用层协议
1表示版本
后面的数字发送的时间,随便写
吴彦祖代表发送方
吴彦祖-pc代表发送方电脑
32代表发送消息
飞秋炸弹:循环不延时发消息(可能会造成卡死)
注意:ip和端口在网络通信中缺一不可,用到的协议也要匹配,例如飞秋用的是udp协议,使用tcp协议发数据是无效的
udp理解为写信(只有收件人地址),tcp理解为打电话(先拨号建立通路,需要通路稳定)
- upd接收数据
from socket import * #导入套接字模块的所有内容 s = socket(af_inet,sock_dgram) # 创建套接字 add = ('192.168.56.1',8081) # 准备接收方地址 str = input('请输入发送的内容》》》') s.sendto(str.encode('gb2312'),add) data = s.recvfrom(1024) # 接收的最大字节数 print(data[0].decode('gb2312')) # 以gb2312字符集进行解码 #发送数据时,python3需要将字符串转换成byte # encode('utf-8) 表示用utf-8对数据进行编码,获得bytes类型对象 # decode()反过来 s.close
- 绑定端口
绑定信息:让一个进程可以使用固定的端口。
一般情况下,发送方不绑定端口,接收方会绑定。
from socket import * #导入套接字模块的所有内容 s = socket(af_inet,sock_dgram) # 创建套接字 s.bind(('',9090)) # 绑定端口9090 add = ('192.168.56.1',8081) # 准备接收方地址 str = input('请输入发送的内容》》》') s.sendto(str.encode('gb2312'),add) data = s.recvfrom(1024) # 接收的最大字节数 print(data[0].decode('gb2312')) # 以gb2312字符集进行解码 #发送数据时,python3需要将字符串转换成byte # encode('utf-8) 表示用utf-8对数据进行编码,获得bytes类型对象 # decode()反过来 s.close
- echo服务器
echo服务器:echo服务是一种非常有用的用于调试和检测的工具。这个协议的作用也十分简单,接收到什么原封发回
```python
from socket import *
udpsocket = socket(af_inet,sock_dgram)
portbind = ('',9090)
udpsocket.bind(portbind)
num = 0
while true:
recvmsg = udpsocket.recvfrom(1024)
print(recvmsg[0].decode('gb2312'))
udpsocket.sendto(recvmsg[0],recvmsg[1])
num += 1
print('已将接收到的第{0}个数据返回'.format(num))
udpsocket.close()
```
''' 聊天室 ''' from socket import * import time udpsocket = socket(af_inet,sock_dgram) # 创建套接字 bindaddr=('',9090) udpsocket.bind(bindaddr) # 绑定端口 while true: recvdata = udpsocket.recvfrom(1024) # 接收数据,最大接收1024个字节 print('时间:【{0}】,对方ip:{1},传来消息内容:{2}'.format(time.ctime(),recvdata[1],recvdata[0].decode('gb2312'))) str = input('请输入要发送的内容》》》') udpsocket.sendto(str.encode('gb2312'),recvdata[1]) udpsocket.close() # 关闭套接字
- udp网络通信过程
udp网络通信过程:(类似于发快递)
- 应用层编写数据(你好),然后向下层传递
- 传输层在数据前面加上端口号(包括发送端口和目的端口)
- 网络层继续在前面加上ip地址(包括原ip和目的ip)
- 链路层再在前面加上mac地址(mac:硬件地址,用来定义网络设备的位置)
此时数据变成了:mac地址 ip地址 端口号 数据内容
之后通过网络传输给另一台计算机的链路层开始逐步解析判断
''' 使⽤多线程完成⼀个全双⼯的聊天程序: 全双工(full duplex)是通讯传输的一个术语。通信允许数据在两个方向上同时传输(电话) 单工是只允许甲方向乙方传送信息,而乙方不能向甲方传送(收音机) 半双工:甲方发消息时乙方只能收不能发(对讲机) ''' from socket import * import time import threading udpsocket = socket(af_inet, sock_dgram) # 创建套接字 bindaddr = ('', 9090) udpsocket.bind(bindaddr) # 绑定端口 addip = ('192.168.56.1', 8081) def send(): while true: str = input('请输入要发送的内容》》》') udpsocket.sendto(str.encode('gbk'), addip) def receive(): while true: recvdata = udpsocket.recvfrom(1024) print(recvdata[0].decode('gbk')) def main(): t1 = threading.thread(target = send) t2 = threading.thread(target = receive) t1.start() t2.start() if __name__=='__main__': main()
50.5 wireshark安装和使用
- wireshark说明
wireshark是一个网络抓包软件: 所有流经本电脑的数据(不论收发),都可以获取(win系统安装时如果出现无法定位输入点。。。需要安装winpcap)
no显示序号:表示已经抓到了多少个数据包
time:表示抓到数据包所经过的时间
sourct:源ip,destination:目的ip,protocol:使用的协议
length:数据包的长度(字节)
info:wireshark对这个数据包的理解
- wireshark过滤规则
过滤规则
过滤ip,如来源ip或者目标ip等于某个ip
例子:
查找目的地址为192.168.101.8的包
ip.dst==192.168.101.8
查找源地址ip.src==1.1.1.1(eq就是==)
ip.src eq 192.168.1.107 or ip.dst eq 192.168.1.107或者ip.addr eq 192.168.1.107 都能显示来源ip和目标ip过滤端口
例子:
tcp.port eq 80 // 不管端口是来源的还是目标的都显示
tcp.port == 80
tcp.port eq 80 or udp.port eq 80
tcp.dstport == 80 // 只显tcp协议的目标端口80
tcp.srcport == 80 // 只显tcp协议的来源端口80
udp.port eq 15000过滤协议:直接输入协议名
例子:
tcp
udp
arp
icmp
http
50.6 tftp介绍
tftp(trivial file transfer protocol,简单⽂件传输协议)是tcp/ip协议簇中的⼀个⽤来在客户端与服务器之间进⾏简单⽂件传输的协议。
使用tftp这个协议,就可以实现简单文件的下载。
特点:
简单/占⽤资源⼩/适合传递⼩⽂件/适合在局域⽹进⾏传递/端⼝号为69/基于udp实现。
有了服务器之后,还需要编写一个下载器(客户端)
实现tftp下载器:
下载:从服务器上将一个文件复制到本机上
下载的过程:
在本地创建一个空文件(与要下载的文件同名)
向里面写数据(接收到一点就向空文件里写一点)
关闭(接受完所有数据关闭文件)
# tftp客户端编程 import struct from socket import * udpsocket = socket(af_inet,sock_dgram) # 创建套接字 serveraddr = ('192.168.56.1',69) # 服务器ip与端口的元组,作为套接字对象sendto方法的第二个参数。 filename = 'test1.jpg' # 文件名 # 调用struct模块的pack方法,按照指定方法将数据转换成字节流字符串 send_data = struct.pack('!h{0}sb5sb'.format(len(filename)),1,filename.encode(),0,'octet'.encode(),0) udpsocket.sendto(send_data,serveraddr) # 向服务器发送请求 f = open(filename,'ab') # 以字节流追加形式打开文件,在当前目录下 while true: recv_data = udpsocket.recvfrom(1024) # 接收数据 caozuoma,act_num = struct.unpack('!hh',recv_data[0][:4]) # 获取操作码和块编号 # random_port = recv_data[1][1] if caozuoma == '5': print('文件不存在') break f.write(recv_data[0][4:]) # 将数据写入 if len(recv_data[0])<516: break ack_data = struct.pack('!hh',4,act_num) udpsocket.sendto(ack_data,recv_data[1]) # 回复ack确认包
50.7 udp广播
import socket server_addr = ('<broadcast>',6868) # <broadcast>自动识别当前的广播ip地址 # 创建套接定对象 s = socket.socket(socket.af_inet,socket.sock_dgram) # 修改套接字的设置,使其可以发送广播 s.setsockopt(socket.sol_socket,socket.so_broadcast,1) # 允许发送广播 #setsocketopt 设置套接字选项 #以广播形式发送数据到本网络的所有电脑中 s.sendto('你好'.encode('gbk'),server_addr) print('等待回复:') print() while true: msg,addr=s.recvfrom(1024) print(addr,msg.decode('gbk'))
50.8 简述tcp
网络通信的方式:tcp、udp。
tcp:传输控制协议(使用情况多于udp)
- 稳定:保证数据一定能收到
- 相对udp会慢一点
- web服务器一般都使用tcp(银行转账,稳定比快要重要)
tcp通信模型:
在通信之前,必须先等待建立链接
在tcp传输过程中,如果有一方收到了对方的数据,一定会发送一个ack确认包给发送方。
长连接:三次握手四次挥手之间分多次传递完所有数据(优酷看视频、在线游戏),长时间占用某个套接字
短连接:三次握手四次挥手之间传递少部分数据,多次握手挥手才传递完所有数据(浏览器),短时间占用
50.9 创建tcp服务器与客户端
创建tcp服务器流程如下:
- socket创建⼀个套接字
- bind绑定ip和port
- listen设置最大连接数,收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求
- accept等待客户端的链接、接收连接请求
- recv/send接收发送数据
# 创建tcp服务器 from socket import * tcpsersocket = socket(af_inet, sock_stream) address = (‘’, 7788) tcpsersocket.bind(address) tcpsersocket.listen(5)#设置最大连接数 newsocket, clientaddr = tcpsersocket.accept() # 如果有新的客户端来链接服务器, 那么就产⽣⼀个新的套接字 # newsocket⽤来为这个客户端服务(10086小妹) # tcpsersocket就可以省下来等待其他新客户端的链接 # 接收对⽅发送过来的数据, 最⼤接收1024个字节 recvdata = newsocket.recv(1024) #接收tcp数据 # 发送⼀些数据到客户端 newsocket.send(“thank you !”) #发送tcp数据 # 关闭为这个客户端服务的套接字, 只要关闭了, 就意味着为不能再为这个客户端服务了 newsocket.close() # 关闭监听套接字, 只要这个套接字关闭了, 就意味着整个程序不能再接收任何新的客户端的连接 tcpsersocket.close() # 使用网络调试助手测试代码
# 创建tcp客户端 from socket import * def clientsocket(): client_socket = socket(af_inet,sock_stream) seraddr = ('192.168.56.1',9090) # 连接服务器 client_socket.connect(seraddr) # 给服务器发送消息 client_socket.send('hello'.encode()) #接收消息 recv_data = client_socket.recv(1024) print(recv_data) client_socket.close() if __name__=='__main__': clientsocket()
上一篇: 让老设备脱胎换骨 升级老电脑该从哪里入手:教你一招
下一篇: Java解析XML的方法