网络编程+Python
阅读原文时间:2023年07月08日阅读:1

一、网络编程(模块:socket,from socket import *):

1. 网络层的IP地址可以唯一标识网络中的主机,传输层的“协议+端口”则可以唯一标识主机中应用程序(进程)。

2. 套接字socket:实现网络中不同主机间应用程序间的通信。类似电话插孔。

3. 服务器:提供服务; 客户端:发出请求,请求服务,一般需要绑定端口,服务器才能向其发数据。

 服务器有:打印机服务器(硬件服务器),文件服务器(硬件服务器),数据库服务器(软件服务器),web服务器(软件服务器)

硬件服务器需要有专用的外围设备,如磁盘驱动器,打印机等。软件服务器提供主要的服务包括程序执行,数据传输检索,更新等。

常见的架构有C/S(Client/Server)(客户机/服务器)和B/S(Browser/Server)Browser/Server(浏览器/服务器)架构。

4. UDP与TCP的区别:

UDP(用户数据报协议),一个无连接的简单的面向数据报的传输层协议,TFTP(简单文件传输协议)就是基于UDP实现的。

特点是:通信时不需要建立连接,可实现广播传输,不提供可靠性,即不保证目的地址能收到数据,且没有超时重传机制,传输数据时有大小限制,每个数据报在64kb以内

优点是:传输速度快

适用情况:多点通信和实时的数据业务,如:视频,QQ,语音广播,局域网中的视频会议系统,保证连贯性和速度即可。

TCP(传输控制协议),提供面向连接的、可靠的字节流服务。

特点是:clien和server交换数据前必须先建立可靠的连接(三次握手),提供超时重发,校验数据等,保证数据能传到目的地址。

适用情况:点对点通信。

5. 单播:点对点        多播:一对多      广播:一对所有,广播地址的最后一位为255

6. OSI模型:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层

四层网络模型:应用层(应用进程),传输层(UDP,TCP),网络层(IP, ICMP,IGMP, ARP,RARP),链路层(网络接口,负责报文传输)

说明:

IP地址:标记逻辑地址   MAC地址:标记实际转发数据时的设备地址,通常由分配厂商的代码和适配器接口扩展标识符组成的。

ICMP : ping网络时使用的协议        ARP:地址解析协议,获取一个电脑的网卡号,Mac地址时使用

RARP:逆地址解析协议, 使只知道自己硬件地址的主机能够知道其IP地址,用的较少。

DNS:域名解释器   NET:网络地址转换器

netmask:子网掩码,和IP地址一起确认网络号或主机号

 不同局域网间通信过程中,IP地址不变,mac地址会在通信中改变,模拟过程可使用Cisco Packet Tracker工具(开源)查看。

7. 长连接和短连接的区别:

长连接:client和server先建立连接,然后不断开,再进行报文发送和接收,适用于频繁连接的需求,常用于P2P通信

建立连接——数据传输——(保持连接)——数据传输——关闭连接,可省去频繁的进行tcp建立和关闭,节约时间,但占用资源,如视频,数据库连接    

短连接: client每次发送报文都要建立连接,通信完就断开连接。常用于一点对多点通信,c/s通信,web网站的http服务一般是短连接,节省资源。

建立连接——数据传输——关闭连接——建立连接——数据传输——关闭连接,如网站访问。

8. UDP网络通信过程:

创建UDP客户端过程:

1 #coding=utf-8
2 from socket import *
3 # import socket
4 # udpsocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
5 #1.创建套接字
6 udpsocket = socket(AF_INET, SOCK_DGRAM)
7 #2. 准备接收方的地址
8 bindaddr = ('', 9090)
9 udpsocket.bind(bindaddr)
10 sendAddr = ('192.168.137.2 ', 8080)
11 #3.从键盘获取数据:
12 sendData = raw_input("请输入要发送的数据:")
13 #4. 发送数据到指定电脑
14 udpsocket.sendto(sendData.encode("gb2312"), sendAddr)
15 recvData = udpsocket.recvfrom(1024)
16 print(recvData[0].decode("gb2312"))
17 print(recvData[1][0].decode("gb2312"))
18 print(recvData[1][1])
19 #5.关闭套接字
20 udpsocket.close()

创建udp服务器过程:

#coding=utf-8
from socket import *
#1.创建套接字
udpsocket = socket(AF_INET, SOCK_DGRAM)
#2. 绑定服务器IP和port
bindaddr = ('192.168.137.2', 8080)
udpsocket.bind(bindaddr)#3.接收数据
while Ture:
  recvData = udpsocket.recvfrom(1024)
print(recvData[0].decode("gb2312")) #打印接收到的数据#4.关闭套接字
udpsocket.close()

基于UDP的多线程聊天,一台电脑的话可以使用NetAssist网络助手(开源)模拟实现,打开多个窗口,每个窗口绑定不同的端口,模拟不同的应用程序,从而模拟多用户收发数据:

1 # -*- coding: utf-8 -*-
2 from threading import Thread
3 #import chardet
4 from socket import *
5 # import sys
6 # reload(sys)
7 # sys.setdefaultencodig('utf-8')
8 #多线程完成聊天
9 #1. 线程recvData 收数据
10 def recvData(udpsocket):
11 while True:
12 recvinfo = udpsocket.recvfrom(1024)
13 print(recvinfo)
14 print(" %s, %s"%(recvinfo[1], recvinfo[0].decode("gb2312")))
15
16 #2. 线程sendData发数据
17 def sendData(udpsocket):
18 sendAddr = ('192.168.137.2 ', 8080)
19 while True:
20 sendinfo = raw_input()
21 #udpsocket.sendto(sendinfo, sendAddr)
22 udpsocket.sendto(sendinfo.decode('utf-8').encode("gb2312"), sendAddr)
23
24 #udpsocket = None #后面存的如果是对象,就定义None
25
26
27 #创建线程
28 def main():
29 # global udpsocket
30 udpsocket = socket(AF_INET, SOCK_DGRAM)
31 setaddr = ("", 9090)
32 udpsocket.bind(setaddr)
33 recvdata = Thread(target=recvData, args=(udpsocket,))
34 senddata = Thread(target=sendData, args=(udpsocket,))
35 recvdata.start()
36 senddata.start()
37 senddata.join()
38 recvdata.join()
39 udpsocket.close()
40
41
42 if __name__ == "__main__":
43 main()

说明: 单工:只能单向发送。 半双工:同一时刻只有一端可以发送/接收    全双工:同一时刻两端均可接收/发送。 本例实现的是全双工。

9. 创建TCP过程:

(1)创建tcp套接字:tcpsocket = socket(AF_INET, SOCK_STREAM)

 (2)绑定IP和端口:  bindaddr = ('192.168.137.2', 8080)

             tcpsocket.bind(bindaddr)

(3)监听连接listen:tcpsocket.listen(5)

(4)接收客户端连接:cs = tcpsocket.accpet()

(5)接收或发送数据,用的是recv和send函数与udp不一样。

(6)关闭客户端套接字:cs.close()

  (7)关闭服务器套接字:tcpsocket.close()

10. TCP三次握手:

  (1)客户端发送一个包含SYN的TCP报文给服务器,该报文中包含客户端使用的端口及TCP连接的初始序号等信息, SYN, seq =xxx。

  (2)服务器接收到SYN报文后,返回一个SYN+ACK报文表示客户端的请求被接收,SYN, seq=y, ACK=xxx+1

  (3)客户端收到SYN+ACK报文后,返回ACK确认包,ACK =y+1

11.  常见网络攻击:

  (1)拒绝式服务攻击(DOS):一直向服务器发送SYN,但是不反馈ACK,直到服务器listen队列满,从而导致正常用户无法访问服务器。TCP连接资源耗尽,停止响应正常请求,如SYN洪水攻击。

  (2)DNS攻击:一种是DNS服务器劫持,即更改域名解析结果,将用户引向错误的地址。另一种是DNS欺骗,即用假的DNS应答来欺骗用户。

  (3)ARP攻击:在服务器和客户端中间插入,拦截服务器发送信息,自己保存后再转发给客户端。

12. HTTP状态码含义:以2开头的状态代码表示成功,以3开头的状态代码表示由于各种不同的原因用户请求被重定向到了其他位置,以4开头的状态代码表示客户端存在某种错误,以5开头的状态代码表示服务器遇到了某个错误。常见的:200 :OK     302: Found    400 :Bad Request  404:Not Found   410:Gone    500:Internal Server Error  501 Not Implemented

二、python基础知识

  1. 基本数据类型有:列表,字符串,元组,字典,数字,还有集合和日期。 元组与列表的区别:元组不可更改,列表可变。

  2. 列表常用方法:append,insert,extend, count, reverse, sort(原列表更改), sorted(原列表的副本更改), remove, pop

  3.  字符串常用方法:join(“分隔符”.join(object)),split,strip(删除两边的空格),replace,zfill(n)(左侧以0填充n个字符)

  4. range与xrange区别:range生成一个列表,而xrange生成一个生成器对象,在数据量大的情况下,xrange性能更好一些,不用开辟大量内存存储列表中的元素,从而节约内存

  5. 多态:一类事物有多种形态,如动物,有猫,狗,鸟等。封装:对象的具体实现等打包成整体,外界不用知道如何实现的也能能使。 继承:子类继承父类,减少重写代码

  6. 迭代器和生成器:具有next方法的容器称为迭代器,含有yeild语句的容器称为生成器,生成器一定是迭代器,迭代器不一定是生成器,生成器保存的是算法,每次调用生成器的时候,都会返回到上次中断的地方(yeild语句处)。生成器会自动创建next和__iter__方法,简洁高效,节省内存。可以使用isinstance()判断一个对象是否是可迭代对象,需要使用到collections模块中的Iterable,如isinstance({},Iterable),返回布尔类型。

举例:列表生成式  x = [ 2*a for a in range(5) ]   生成器:x = (2*a for a in range(5) )  next(x)

  7. 闭包和装饰器:

  闭包:一个函数内再定义一个函数,内层函数会引用外层函数作用域的变量,则内部函数就是闭包。作用:提高代码的可复用性,减少代码的可移植性。(如果不使用闭包,用两个独立的函数,则可能需要重复说明某些参数)

举例说明:

1 def bibao(num1):
2 num1 += 10
3 def func(num2):
4 print("%d + %d = %d"%(num1, num2, num1+num2)) #内层函数有用到外层函数的局部变量,该内层函数就是一个闭包
5 return num1+num2
6 return func
7
8
9 ret = bibao(10)
10 print ret(20)

  装饰器:格式:@函数名 (语法糖)紧挨着需要装饰的函数上方。作用:引入日志,函数执行时间统计,执行函数前预处理和执行后的清理功能,权限校验等场景,缓存。举例说明如下:

1 #encoding=utf-8
2 from time import ctime, sleep
3 def timeFun(pre):
4 def innerTimeFun(func):
5 def wrappedFunc():
6 print ("%s wrote at %s"%(pre, ctime()))
7 return func()
8 return wrappedFunc
9 return innerTimeFun
10
11 @timeFun("Mrs Peng")
12 def hello():
13 print("hello!")
14
15 hello()

分析:程序先运行15行,发现有11行的装饰器,11行程序处理步骤为:先 执行timeFun("Mrs Peng"),程序跳到第3行函数,第3行函数返回innerTimeFun这个函数的引用,再执行@innerTimeFun,用其对hello()函数进行装饰,等价于 hello = innerTimeFun(hello),运行结果如下:

下面是使用装饰器去统计函数执行时间的例子:

1 #encoding=utf-8
2 from time import time, sleep
3 """函数执行时间统计"""
4 def timeCount(func):
5 def intime():
6 starttime = time()
7 func()
8 curtime = time()
9 usetime = curtime - starttime
10 return usetime
11 return intime
12
13
14 @timeCount
15 def test():
16 print("Now it is beginning!")
17 sum = 0
18 for i in range(10000):
19 sum += i
20 print(sum)
21 print ("Now it is over!")
22
23
24 print(test())

  8. is 和==的区别:is 是比较两个引用是否指向同一个对象,而==是看左右两边的值是否一样。

  9.  深拷贝和浅拷贝:浅拷贝仅仅是拷贝了对象的引用,可以类似理解成windows桌面的快捷方式,一个应用可以创建多个快捷方式,但都指向同一个应用。浅拷贝,更改任何一个引用的内容,则所有指向该对象的引用及对象本身都会同步被更改,即你动我也动。另外,浅拷贝对于可变对象(如列表,字典等)来说,拷贝了对象的引用,且id号会变,而对不可变对象如元组等来说,就是拷贝引用,id号不变。深拷贝,是完全的拷贝对象,即重新开辟一个内存地址,该地址存储的类型及内容与原对象一样,只是地址不一样,若改变其中某个,另一个不会随之改变,即你动我不动。举例说明:列表的复制,使用分片来实现复制,是深拷贝;将某变量x赋值给变量y,属于浅拷贝,x,y均指向同一对象。如下:

a = [1,3,5,7,9]
b = a[:]
c = a
a.append(11)
print a, id(a)
print b, id(b)
print c, id(c)

运行结果如下,从结果中可知,b和a的id不一样,内容也不一样,是深拷贝。a与c一样,属于浅拷贝。如果需要使用copy和deepcopy函数需要从模块copy中导入这两个方法。

10. 进制间的转换:如:将十进制转换成二进制、八进制、十六进制:bin(18),oct(18), hex(18). 将二进制、八进制、十六进制转换成十进制:int("ob10010",2)、int("022",8)、int("0x12",16)。IP地址转换为十进制时会使用到。

11. python内存管理:

  垃圾回收:

  (1)小整数对象池:[-5, 257]区间为小整数,这些整数对象是提前建立好,不会被垃圾回收,小整数和单个字符共用对象,常驻内存。

  (2)单个单词,默认开启Intern机制,共用对象,引用计数,当引用计数为0时,则销毁。

  (3)字符串(含空格),没有开启intern机制,不共用对象,引用计数为0,则销毁。

python采用的GC机制(垃圾回收机制),主要是引用计数为主,隔代回收为辅。引用计数机制的缺点:维护引用计数消耗资源,同时循环引用会导致内存泄漏。引用计数就是指当对象的引用计数为0时则清理该对象的内存,隔代回收是指python会检测循环引用的零代、一代和二代链表,每有一个引用被销毁,引用计数减1,一旦零代链表的计数为0的时候,就彻底销毁其内存。

12. python常用模块:

(1)Time:时间相关的,常用方法:ctime, asctime,time,sleep。

(2)re:正则表达式模块,常用方法:match,findall, serach,sub, split。

(3)Random:随机数模块,常用方法:range,xrange,shuffle

(4)threading(Multiprocessing):多线程(多进程)模块,常用方法:Thread,start, join(等待子进程结束后父进程才结束), run

(5)heapq:堆操作模块,常用方法:heappush, heappop, nlargest

(6)OS:操作系统模块,创建子进程方法:folk , getpid。

13. 进程和线程:

  多任务:操作系统同时运行多个任务。

  程序:代码写好,但没运行的时候称为程序。

  进程(multiprocessin模块中Process创建,进程池:Pool):正在运行的代码。是操作系统进行资源分配和调度的基本单位,每个进程都独享系统的一部分资源,进程间的资源不共享,各进程间的变量是相互独立的。进程间通信方式:消息,队列,信号量,一个进程可以包含多个线程,同一个进程中的不同线程共享该进程的环境资源。

  线程(threading中的Thread):线程是进程的一个执行单元或实体,是CUP调度和分派的基本单位,线程间的共享变量,线程相比进程需要的资源较少(PC(程序计数器),寄存器和栈)。多线程程序执行顺序是不确定的。五种状态:新建、就绪、运行、等待、死亡。

  区别:一个程序中至少有一个进程,一个进程中至少有一个线程。

     线程所需的资源比进程少,使得多线程程序的并发性高。(并发:看上去是一起执行,任务数>核数。并行:真正的一起运行)

     进程执行中拥有独立的内存资源,进程间变量相互独立,而多个线程是共享内存,共享变量的,从而提高了程序的运行效率。

     线程不能独立执行,必须存在进程中。进程的开销比线程多。

  可参考https://blog.csdn.net/afei__/article/details/80468265,很形象。

  进程间通信举例如下:

1 #encoding=utf-8
2 from multiprocessing import Process, Queue
3 import os, time, random
4 """进程间通信-消息队列"""
5 def writeDates(q):
6 for value in ["a", "b","c"]:
7 q.put(value)
8 time.sleep(random.random())
9
10 def readDates(q):
11 while True:
12 if not q.empty():
13 value = q.get()
14 print("%s"%value)
15 time.sleep(random.random())
16 else:
17 break
18
19
20 if __name__=="__main__":
21 q = Queue() #创建Queue队列
22 writeProcess = Process(target=writeDates, args=(q,))
23 readProcess = Process(target=readDates, args=(q,))
24 writeProcess.start()
25 writeProcess.join()
26 readProcess.start()
27 readProcess.join()

14. GIL:全局解释器,此锁能保证每次只有一个线程真正在运行。

15. 同步:协同步调,按预先的先后次序进行运行,A做完,B接着开始。如:A进程(线程)运行到一定程度,需要B进程(线程)执行完才能执行。这就是同步。

  当多个线程同时修改某个共享数据时,需要同步控制,常见的同步控为互斥锁。

16. 死锁:举例说明,A拥有锁a,B拥有锁b,A需要B释放锁b才能继续运行,B需要A释放锁a才能进行运行,相互持有锁而不释放,等待对方释放,这就是死锁。避免死锁的方法:银行家算法,或添加超时等待,即看门狗。

互斥锁:A拥有锁a,相应资源“锁定”,B不能对其进行操作。Lock模块,acquire方法获取锁,release释放锁。

17. 生产者和消费者模式

  生产者:生产数据的线程。消费者:消费数据的线程。生产者消费者模式是通过一个容器(阻塞队列相当于一个缓冲区)来解决的是生产与消费速度不相匹配(强耦合)的问题。

18. re模块:

  (1)match函数:语法格式match(str1, str2)从str2中匹配是否含有str1,若有测返回对象,无返回None。注意是匹配str2是否以str1开头,如:str1 = abc  str2=babc  则返回None,显示未匹配成功。若str2=abcd,则匹配成功,可用group显示结果。

  (2)search 函数:语法格式search(str1, str2)从str2中匹配是否含str1,与match的区别是,search是从从头开始匹配,只要匹配到有str1即可。

  (3)findall 函数:遍历匹配,可以匹配出str2中所有包含str1的字符串,返回一个列表。

  贪婪和非贪婪:所谓贪婪就是尽可能多的匹配,非贪婪就是尽可能匹配少的字符。非贪婪操作符?用在“?”,“+”,“*”后面,要求正则匹配的越少越好。

1 #encoding=utf-8
2 import os
3 import re
4 str = "how How are how you"
5 destr = "how"
6 # match匹配,字符串开头。匹配到返回匹配对象,没匹配到,返回None。
7 matchRes = re.match(destr, str)
8 if matchRes is not None:
9 print("match: %s"%matchRes.group())
10 # search匹配,匹配首次搜到到字符串destr,匹配到返回匹配对象,没匹配到,返回None。
11 str2 = "How how are how you"
12 searchRes = re.search(destr, str2)
13 if searchRes is not None:
14 print("search: %s"%searchRes.group())
15 # findall匹配,匹配到所有结果以列表的形式返回。
16 findallRes = re.findall(destr, str, re.I)
17 print("findall: %s"%findallRes)
18 #finditer匹配,匹配到所有结果返回的是一个迭代器。
19 finditerRes = re.finditer(destr, str, re.I)
20 print("finditer:"),
21 try:
22 while True:
23 g = finditerRes.next()
24 print("%s"%g.group()),#python 2.x不换行用","
25 # print("finditer: %s" % g.group(), end=" ")#python 3.x不换行:end = " "
26 except StopIteration:
27 pass