Python网络编程笔记
01. UDP(user datagram protocol)用户数据报协议
	  01. 特点
		    01. 无连接
		    02. 不可靠
		    03. 每个被传输的数据报必须限定在64KB之内
  02. 优点:效率高s
		    缺点:不可靠
  03. 使用场景:多点通讯和实时的数据业务
		    语音广播
		    视频传输
		    QQ
		    TFTP
		    SNMP
		    RIP
		    DNS
  04. udp服务器编写
		    server_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		    server_sock.bind(("", 8080))
		    while 1:
		          recv_data, client_addr = server_sock.recvfrom(1024)
		          print("收到了客户端{0}传来的数据{1}".format(client_addr, recv_data.decode()))
		      # recv_data = server_sock.recvfrom(1024)
		      # server_sock.close()
  05. udp客户端编写
		    client_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
		    client_sock.sendto(b"haha", ('127.0.0.1', 8081))
		    client_sock.close()
  06. udp广播功能
		    要想让socket具有广播功能,
		    首先应该让socket开启广播功能,
		    然后要绑定广播地址,广播地址可以使用ifconfig获得,或者使用'<broadcast>'
		    示例:
			      client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
			      # 设置socket对象,将socket的广播功能打开
			      # socket.SOL_SOCKET 表示设置的选项参数的等级  Set Option Level - SOCKET
			      # socket.SO_BROADCAST 表示设置的具体是哪个选项参数, Set Option - Broadcast 广播
			      client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
			      # 对于客户端可以不用绑定地址信息,操作系统会随机分配一个端口用以通讯使用
			      while True:
			            msg = input("请输入要发送的内容:")   # 字符串类型, 通过msg.encode() 编码 转换为bytes类型
			            # receiver_address = ("192.168.70.255", 8000)  # 接收方 设置广播地址
			            # 使用<broadcast>可以指明广播地址,但是不用固定死,可以达到通用
			            receiver_address = ("<broadcast>", 8000)  # 接收方 设置广播地址
			            client_socket.sendto(msg.encode(), receiver_address)
			        # # 如果不再使用套接字进行通讯,关闭套接字
			        # client_socket.close()
02. socket介绍
	  01. 介绍
		    socket模块的socket类用来发送接收消息,中文名套接字
	  02. socket类的常用方法
		    01. socket.socket(AddressFamily, Type)
			    参数1:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
			    参数2:可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
    02. s.bind(address)
			      给套接字绑定地址,一般服务器需要绑定地址,客户端可以不用绑定
			      address是一个元组,第一个元素是ip地址字符串类型,第二个元素是端口号整数类型
    03. s.sendto()
			      一般用来给udp服务器发送数据
			      参数1: 	要发送的数据,字节类型
			      参数2: 	接收数据的地址,是一个元组,包括ip和端口号
    04. s.send()
			      给tcp服务器发送数据,受TCP流量控制机制的影响,有的时候会阻塞
			      参数:要发送的数据,是字节类型
    05. s.recv()
			      接收客户端发来的数据,默认是阻塞方式一直到客户端发来数据为止,一般用在TCP通信
			      参数:表示接收的最大字节数
			      返回值:接收到的数据,是字节类型,如果收到的数据是长度为0的数据,则表示客户端关闭了连接,此时也可以把服务端的socket关闭。
    06. s.recvfrom()
			      接收客户端传来的数据,默认是阻塞行为,一般用在udp
			      参数:表示接收的最大字节数
			      返回值:
				        返回值1表示接收的数据,是字节类型
				        返回值2表示传来数据的客户端的信息,是一个元组,包括ip和端口
    07. s.listen()
			      开启TCP服务器的监听,使其能够接受客户端的连接请求
			      参数 表示监听队列的大小,即最多能同时处理三次握手的客户端的数量,整数类型
    08. s.accept()
			      接受TCP客户端的请求,与客户端完成三次握手,默认是阻塞方式,等待客户端发起连接
			      返回值1: 	一个新的socket对象,用来与客户端进行一对一的数据传输
			      返回值2: 	建立连接的客户端的地址,是一个元组,包括ip和端口号
    09. s.connect()
			      连接TCP服务器,完成三次握手
			      参数 	服务器的地址,是一个元组,第一个元素是服务器的ip地址,第二个元素是服务器的端口号
    09.s.close()
			      关闭socket,释放资源
    10. s.setblocking()
			      设置socket是否是阻塞方式
			      参数 	False表示以非阻塞方式运行,默认是True,阻塞方式
			      如果设置了非阻塞方式,则accept recv send都会变成非阻塞方式
			      accept在有客户端发起连接的时候,则接受连接,返回新的socket对象和客户端的地址,如果没有客户端发起连接则会抛出BlockingIOError
			      recv在有数据的时候传来的时候,接收数据,否则BlockingIOError异常
    11. s.setsockopt()
			      对socket进行设置
			      参数1 设置的选项的级别
			      参数2 设置的具体选项
			      参数3 1表示开启参数2的选项,0表示关闭
			      常用的选项设置
			      s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) #开启udp socket的广播功能
			      s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
				        #默认情况下socket在关闭后,一段时间内操作系统会禁止再使用之前绑定的端口,
				        对于服务器而言,应该随时都能开启并使用固定的端口,所以应该开启socket随时
				        都可以重用端口号的功能。
    12. s.getpeername()
			        获取与socket s通信的socket对象的信息,是一个元组,包括ip和端口号
03. nc命令的使用
	  01. 作用
		    nc可以作为server以TCP或UDP方式侦听指定端口
		    nc可以作为client发起TCP或UDP连接
  02. 选项
		    -l
			      用于指定nc将处于侦听模式。指定该参数,则意味着nc被当作server,侦听并接受连接,而非向其它地址发起连接。
		    -u
			      指定nc使用UDP协议,默认为TCP
  03 示例
		    nc -u 127.0.0.1 8080 #以udp的方式连接本机8080端口的进程
		    nc -ul 8080			#开启一个udp服务器,使用8080端口
		    nc -l 8080			#开启一个tcp服务器,使用8080端口
		    nc 127.0.0.1 8080		#以TCP的方式连接本机8080端口的进程
04. TCP协议(传输控制协议)
	  01. 特点
		    面向连接的		TCP在传输数据之前要经过三次握手,数据传输完后要有四次挥手的过程
		    可靠的			1. 发送应答 2. 超时重传 3, 错误校验 4. 流量控制和阻塞管理 来实现可靠连接
		    基于字节流的
			      udp是用户数据报,发送接收的数据是一个整体
			      TCP会把数据先缓存到缓存区,然后再进行分包发送
			      在接收端会把收到的包缓存好后,再给程序
			      传输的数据包是以字节大小来编号
  02. TCP服务器编写流程
		    1. 创造一个用以TCP通信的监听socket对象
		    2. 给socket绑定地址
		    3. 调用listen方法开启监听
		    4. 调用accept方法接受客户端的连接,并返回一个新的用来和这个客户端一对一传输数据的socket
		    5. 调用accept返回的新的socket的recv方法接收数据
		    6. 调用返回的新的socket的send方法向客户端发送数据
		    7. 用完之后关闭这个心的socket对象
		    8. 关闭监听的socket对象
  03. TCP服务器代码示例
		    listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		    listen_sock.bind(("", 8080))
		    listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 设置端口号重用
		    listen_sock.listen(128)  #设置最大监听数量
		    new_sock, client_addr = listen_sock.accept()
		    while 1:
		          recv_data = new_sock.recv(1024)
		          if recv_data:
		                print("收到了{0}发送来的数据{1}".format(client_addr, recv_data.decode()))
		          else:
		                # 客户端关闭了连接
		                new_sock.close()
		                break
  04. TCP客户端编写流程
		    1. 创造一个用以TCP通信的监听socket对象
		    2. 调用connect方法连接服务器
		    3. 调用send方法给服务器发送数据
		    4. 调用recv方法接收服务器返回的数据
		    5. 使用完毕调用close方法关闭连接
  05. TCP客户端代码示例
		    import socket
		    client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		    client_sock.connect(('127.0.0.1', 8080))
		    client_sock.send(b'heh')
		    client_sock.close()
05. 服务器模型
	  01. 介绍
		    服务器模型就是能同时处理多个请求,每个连接可以多次发送接收数据的服务器
  02. 多进程版TCP服务器模型
			      from multiprocessing import Process
			      import socket
      listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			      listen_sock.bind(("", 8080))
			      listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
			      listen_sock.listen(128)
      def handle_client(new_sock, client_addr):
			          while 1:
			                recv_data = new_sock.recv(1024)
			                if recv_data:
			                      print("客户端{0}发送过来了消息{1}".format(client_addr, recv_data.decode()))
			                      new_sock.send(recv_data)
			                else:
			                      print("客户端{0}关闭了连接".format(client_addr))
			                      new_sock.close()
			                      break
      while True:
			            new_sock, client_addr = listen_sock.accept()
			            p = Process(target=handle_client, args=(new_sock, client_addr))
			            p.start()
			            new_sock.close()  # 因为new_sock的资源已经复制到了另一个进程里面,所以这里关闭也不会发送fin结束消息,会等进程里面的调用close时才真正关闭
  03. 多线程版TCP服务器模型
		    from threading import Thread
		    import socket
    listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		    listen_sock.bind(("", 8080))
		    listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
		    listen_sock.listen(128)
    def handle_client(new_sock, client_addr):
		          while 1:
		                recv_data = new_sock.recv(1024)
		                if recv_data:
		                      print("客户端{0}发送过来了消息{1}".format(client_addr, recv_data.decode()))
		                      new_sock.send(recv_data)
		                else:
		                      print("客户端{0}关闭了连接".format(client_addr))
		                      new_sock.close()
		                      break
    while True:
		          new_sock, client_addr = listen_sock.accept()
		          t = Thread(target=handle_client, args=(new_sock, client_addr))
		          t.start()
  04. 单进程单线程TCP服务器模型_非阻塞模式
		    listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		    listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # 先设置地址重用,再绑定地址
		    listen_sock.bind(("", 8080))
		    listen_sock.setblocking(False)
		    listen_sock.listen(128)
    client_info = []
		    while True:
		          try:
		                new_sock, client_addr = listen_sock.accept()
		          except BlockingIOError as e:
		                pass
		          else:
		                new_sock.setblocking(False)
		                client_info.append((new_sock, client_addr))
		          to_delete_list = []
		          for sock, cli_addr in client_info:
		              try:
		                    recv_data = sock.recv(1024)
		              except BlockingIOError as e:
		                    pass
		              else:
		                    if recv_data:
		                          print("收到了{0}发送来的信息{1}".format(cli_addr, recv_data.decode()))
		                    else:
		                          to_delete_list.append((sock, cli_addr))
		                          sock.close()
		                          print("客户端{0}关闭了连接".format(cli_addr))
      for item in to_delete_list:
		                client_info.remove(item)
  05 单进程单线程TCP服务器模型_select方式
		    01. 介绍:
			      select是由操作系统提供,由Python进行了封装。select采用轮询的方式对输入的socket列表进行遍历,
			      然后返回可以accept,recv、send、或者出现出现错误的socket,这样我们就可以对返回的socket直接进行操作
			      而不会出现堵塞。这样就可以通过循环来实现多个客户端的连接访问。
			      由于socket在Linux操作系统里是一个文件,select做的工作就是对给定的文件进行轮询,看看哪些文件可以读,哪些文件
			      可以写,哪些文件存在异常,对应在socket就是哪些文件可以accept、recv(读) send(写)
			      由select模块的select方法提供
			      rlist, wlist, xlist = select.select(rlist, wlist, xlist)
			      参数1: 	要让select进行查询是否可以recv accept的socket对象的文件编号列表,由socket对象的fileno()提供
			      参数2: 	要让select进行查询是否可以send的socket对象的文件编号列表
			      参数3: 	要让select进行查询是否有异常socket对象的文件编号列表
			      已上三个参数如果只使用其中的一个或者两个,其他的给个空列表就可以了
      返回值1: 	可以进行recv accept的socket对象的文件编号列表
			      返回值2: 	可以进行send的socket对象的文件编号列表
			      返回值3: 	有异常发生的socket对象的文件编号列表
该方法如果没有查询到符合条件的socket,就会一直堵塞在这里,不过完全不影响
    02. 使用select实现服务器模型
			      import socket
			      import select
      listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			      listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
			      listen_sock.bind(("", 8080))
			      listen_sock.listen()
      sock_dict = {}  # 存放socket和对应的文件编号的字典
			      rlist = [listen_sock.fileno()]  # 可以读取的socket列表
      while True:
			            recv_sock_list, send_sock_list, except_sock_list = select.select(rlist, [], [])
			            for sockfd in recv_sock_list:
			                  if sockfd == listen_sock.fileno():
			                        new_sock, client_addr = listen_sock.accept()
			                        rlist.append(new_sock.fileno())
			                        sock_dict[new_sock.fileno()] = new_sock
          else:
			                        recv_data = sock_dict.get(sockfd).recv(1024)
			                        if recv_data:
			                              print("收到了{0}传来的数据:{1}".format(sock_dict.get(sockfd).getpeername(),recv_data.decode()))
			                        else:
			                              print("客户端{0}关闭了连接".format(sock_dict.get(sockfd).getpeername()))
			                              sock_dict.get(sockfd).close()  # 关闭服务端的socket
			                              rlist.remove(sockfd)  # 从轮询列表中删除这个socket
			                              del sock_dict[sockfd]  # 从字典中删除这个socket
  06. 单进程单线程TCP服务器模型_epoll方式
  	    01. epoll介绍
  		      没有最大并发连接的限制,能打开的FD(指的是文件描述符,通俗的理解就是套接字对应的数字编号)的上限远大于1024
			      效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数;即epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,epoll的效率就会远远高于select。
			      epoll使用的事件机制,只有socket对应的条件发生了,才会通知程序。
			      通过select模块的epoll类实现所有方法
		    02. epool版服务器模型代码示例
			      import socket
			      import select
      listen_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			      listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
			      listen_sock.bind(("", 8080))
			      listen_sock.listen()
      sock_dict = {}
			      epoll = select.epoll()
epoll.register(listen_sock.fileno(), select.EPOLLIN)
      while 1:
			            sock_fd_list = epoll.poll()
			            for sock_fd, event in sock_fd_list:
			                  if sock_fd == listen_sock.fileno():
			                        conn, client_addr = listen_sock.accept()
			                        print("客户端{0}已连接".format(client_addr))
			                        sock_dict[conn.fileno()] = conn
			                        epoll.register(conn.fileno(), select.EPOLLIN)
			                  else:
			                        if event == select.EPOLLIN:
			                              sock = sock_dict[sock_fd]
			                              recv_data = sock.recv(1024)
			                              if recv_data:
			                                    print("客户端{0}传来了数据{1}".format(sock.getpeername(), recv_data.decode()))
			                              else:
			                                    print("客户端{0}关闭了连接".format(sock.getpeername()))
			                                    sock.close()
			                                    del sock_dict[sock_fd]
			                                    epoll.unregister(sock_fd)
    03. epool常见方法介绍
			      01. epoll = select.epoll()
				        创造一个epoll对象
      02. epoll.register()
				        把socket对象注册给epool来监听
				        参数1 	注册的socket对象的文件编号,通过fileno()方法获得
				        参数2 	注册的事件类型,即让epoll监视是能读还是能写,对应socket就是recv accept(读) send(写)
					          EPOLLIN表示要进行input监控,即监视什么时候能够执行recv或者accept操作
					          EPOLLOUT表示要进行output监控,即监视什么时候能够执行send操作
					          EPOLLET (ET模式)
						            epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:
							              LT模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll时,会再次响应应用程序并通知此事件。
							              ET模式:当epoll检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll时,不会再次响应应用程序并通知此事件。
			      03. epoll.poll()
				        询问epoll是否有socket符合条件,阻塞方式直到有socket符合条件
				        返回一个列表,列表中每个元素是一个元组,元组中元素1是符合条件的socket对象的文件编号,元素2是事件名,即调用register方法时传入的时间类型
    04. epoll.unregister()
				      把socket对象从epoll中取消监视
				      参数 	要取消注册的socket对象的文件编号
07. 单进程单线程TCP服务器模型_协程方式
  08. 几种服务器模型对比总结
		    本来用户态处理网络请求应该如同像内核响应外设中断一样,每一个请求分配一个独立堆栈(linux x86只有4K x64只有8k)来处理....
		    结果传统用户态处理网络请求的服务,要么是来个请求就分配一个可被内核独立调度的单元(能独立调度的单元自然含有独立堆栈)来处理,如线程,进程等;
		    要么是写一个巨大无比的状态机。
		    前者由于线程和进程本身的职能不仅仅是给分配一个堆栈,造成资源和调度的巨大浪费;
		    后者本质类似用一个驱动程序处理了系统所有外设中断(这比Linux宏内核耦合度要高得多:要知道任何一个被epoll管理的fd的可用(或者超时),都本质代表一个IO中断(或者时钟中断),
		    比这更耦合的我只能想到:用一个函数写完一个操作系统内核。。), 
		    那代码维护起来难度极大极大的,而且想改一下,很容易触碰到不相关的逻辑。
		    协程就是一个把 每个请求用轻量级堆栈处理+每个请求的处理在用户态彼此协作式调度
		    这2个思想融合的产物。
		    前者让资源使用率低和代码逻辑解耦,后者让调度效率高且不需要发起系统调用来调度。
05. 其他小知识点
	  01. DNS使用53号端口,是以udp的方式
	  02. 回显服务器:接收客户端发送的数据,原封不动发送给客户端
	  03. 端口号:16位二进制数表示,一个65536个,0-1023是系统预分配好的,一般不要使用
Python网络编程笔记的更多相关文章
- Python网络编程笔记二
		
使用select模块实现IO多路复用服务端 import socket import select #windows上只支持select.select,不支持poll epoll HOST = &qu ...
 - Python网络编程笔记一
		
AF_INET:IPV4 AF_INET6:IPV6 套接字类型: SOCK_STREAM:TCP SOCK_DGRAM:UDP 创建TCP套接字,也可以不传递参数,默认创建TCP套接字 tcpSoc ...
 - 《Python网络编程》学习笔记--使用谷歌地理编码API获取一个JSON文档
		
Foundations of Python Network Programing,Third Edition <python网络编程>,本书中的代码可在Github上搜索fopnp下载 本 ...
 - python核心编程--笔记
		
python核心编程--笔记 的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找pyt ...
 - Python 网络编程(二)
		
Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单 ...
 - Python 网络编程(一)
		
Python 网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. ...
 - Python学习(22)python网络编程
		
Python 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的 ...
 - Day07 - Python 网络编程 Socket
		
1. Python 网络编程 Python 提供了两个级别访问网络服务: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...
 - python网络编程-01
		
python网络编程 1.socket模块介绍 ①在网络编程中的一个基本组件就是套接字(socket),socket是两个程序之间的“信息通道”. ②套接字包括两个部分:服务器套接字.客户机套接字 ③ ...
 
随机推荐
- Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)
			
整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道 ...
 - 利用MATLAB进行曲线拟合
			
软件环境:MATLAB2013a 一.多项式拟合 多项式拟合是利用多项式最佳地拟合观测数据,使得在观测数据点处的误差平方和最小. 在MATLAB中,利用函数ployfit和ployval进行多项式拟合 ...
 - 第三百三十四节,web爬虫讲解2—Scrapy框架爬虫—Scrapy爬取百度新闻,爬取Ajax动态生成的信息
			
第三百三十四节,web爬虫讲解2—Scrapy框架爬虫—Scrapy爬取百度新闻,爬取Ajax动态生成的信息 crapy爬取百度新闻,爬取Ajax动态生成的信息,抓取百度新闻首页的新闻rul地址 有多 ...
 - MyBatis环境配置及入门
			
Mybatis 开发环境搭建,选择: MyEclipse8.5 版本,mysql 5.5, jdk 1.8, mybatis3.2.3.jar 包.这些软件工具均可以到各自的官方网站上下载. 整个过程 ...
 - can 驱动
			
http://www.cnblogs.com/general001/articles/2342728.html http://blog.csdn.net/luoqindong/article/deta ...
 - (转)st(state-threads) coroutine调度
			
目录(?)[-] EPOLL和TIMEOUT TIME TIMEOUT Deviation st(state-threads) https://github.com/winlinvip/state ...
 - Servlet下载文件迅雷不支持问题真相之一
			
问题描述 最近在做一个下载文件的Servlet,直接使用浏览器的下载功能,完美支持,结果测试人员使用迅雷下载,就不行了,下载也能成功完成,只是迅雷下载的文件大小是悲催的0KB 真相搜罗 网上有很多帖子 ...
 - 用js实现table内容从下到上连续滚动
			
网上有很多用ul实现新闻列表滚动的例子,但是很少有直接用table实现列表内容滚动的例子,而Marquee标签滚动的效果不是很好,于是就自己写了一个,提供给攻城师朋友们参考 实现思路:由于table包 ...
 - Redis 缓存 + Spring 的集成示例(转载)
			
1. 依赖包安装 pom.xml 加入: <dependency> <groupId>org.springframework.data</groupId> < ...
 - Service 保活法之一
			
我们都知道,在Android中,Service有两种启动方式: startService 以startService()启动服务,系统将通过传入的Intent在底层搜索相关符合Intent里面信息的s ...