网络编程
网络编程需要知道的概念
网络体系结构就是使用这些用不同媒介连接起来的不同设备和网络系统在不同的应用环境下实现互操作性,并满足各种业务需求的一种粘合剂。网络体系结构解决互质性问题彩是分层方法。
1.网络(OSI)的7层模型:
应用层--->为应用程序提供网络通信服务
表示层--->数据表示
会话层--->主机间通信(两个应用进程间)
传输层--->端到端的连接,隔离网络的上下层协议,使得网络应用与下层协议无关
网络层--->寻找最优路径,转发数据包
数据链路层--->无差错的链路连接
物理层--->二进制传输
2.端口
是一种抽象的软件结构,包括一些数据结构和I/O缓冲区。与协议有关。
3.套接字存在于通信区域中。
通信区域也叫地址族,它是一个抽象的概念,主要用于将通过套接字通信的进程的共有特性综合在一起。
为保证数据的正确性,在网络协议中需要制定网络字节顺序,采用统一的网络字节顺序。
网络通信三要素:
IP地址:用于表示主机(IP地址 = 网络ID+主机ID)
端口号:用于标识进程的逻辑端口
传输协议:TCP UDP
网络通信过程就是一个不断封装和解析的过程
Socket是连接应用程序与网络驱动程序的桥梁,Socket在应用程序中创建,通过绑定操作与驱动程序建立关系。
套接字
套接字是为特定网络协议(例如TCP/IP,ICMP/IP,UDP/IP等)套件对上的网络应用程序提供者提供当前可移植标准的对象。它们允许程序接受并进行连接,如发送和接受数据。为了建立通信通道,网络通信的每个端点拥有一个套接字对象极为重要。
套接字为BSD UNIX系统核心的一部分,而且他们也被许多其他类似UNIX的操作系统包括Linux所采纳。许多非BSD UNIX系统(如ms-dos,windows,os/2,mac os及大部分主机环境)都以库形式提供对套接字的支持。
三种最流行的套接字类型是:stream,datagram和raw。stream和datagram套接字可以直接与TCP协议进行接口,而raw套接字则接口到IP协议。但套接字并不限于TCP/IP。
套接字模块 -------SOCKET()模块
套接字模块是一个非常简单的基于对象的接口,它提供对低层BSD套接字样式网络的访问。使用该模块可以实现客户机和服务器套接字。要在python 中建立具有TCP和流套接字的简单服务器,需要使用socket模块。利用该模块包含的函数和类定义,可生成通过网络通信的程序。
SOCKET内建方法
| 函数 | 描述 |
| 服务器端套接字函数 | |
| s.bind() |
绑定地址(主机,端口号对)到套接字 s.bind(address) 将套接字绑定到地址。 address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。 |
| s.listen() |
开始TCP 监听 开始监听传入连接。添加参数是指定在拒绝连接之前,可以挂起的最大连接数量。 如果参数等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5 |
| s.accept() |
被动接受TCP 客户的连接,(阻塞式)等待连接的到来 接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。 接收TCP 客户的连接(阻塞式)等待连接的到来 |
| 客户端套接字函数 | |
| s.connect() |
主动初始化TCP 服务器连接,参数为地址(address) 连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 |
| s.connect_ex() |
connect()函数的扩展版本,出错时返回出错码,而不是抛异常 同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061 |
| 公共用途的套接字函数 | |
| s.recv() |
接收TCP 数据 sk.recv(bufsize[,flag]) 接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。 |
| s.send() |
发送TCP 数据 将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。 |
| s.sendall() |
完整发送TCP 数据 将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 内部通过递归调用send,将所有内容发送出去。 |
| s.recvfrom() |
接收UDP 数据 与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 |
| s.sendto() |
发送UDP 数据 将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。 |
| s.getpeername() |
连接到当前套接字的远端的地址 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 |
| s.getsockname() |
当前套接字的地址 返回套接字自己的地址。通常是一个元组(ipaddr,port) |
| s.getsockopt() | 返回指定套接字的参数 |
| s.setsockopt() | 设置指定套接字的参数 |
| s.close() | 关闭套接字 |
| 面向模块的套接字函数 | |
| s.setblocking() |
设置套接字的阻塞与非阻塞模式 是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。 |
| s.settimeout()a |
设置阻塞套接字操作的超时时间 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s ) |
| s.gettimeout()a | 得到阻塞套接字操作的超时时间 |
| 面向文件的套接字的函数 | |
| s.fileno() | 套接字的文件描述符 |
| s.makefile() | 创建一个与该套接字关连的文件 |
建立服务器连接需要六个步骤:
1.创建socket对象。调用socket构造函数。
socket=socket.socket(familly,type)
family的值可以是AF_UNIX(Unix域,用于同一台机器上的进程间通讯),也可以是AF_INET(对于IPV4协议的TCP和 UDP),至于type参数,SOCK_STREAM(流套接字)或者 SOCK_DGRAM(数据报文套接字),SOCK_RAW(raw套接字)。
2.则是将socket绑定(指派)到指定地址上,socket.bind(address)
address必须是一个双元素元组,((host,port)),主机名或者ip地址+端口号。如果端口号正在被使用或者保留,或者主机名或ip地址错误,则引发socke.error异常。
3.绑定后,必须准备好套接字,以便接受连接请求。
socket.listen(backlog)
backlog指定了最多连接数,至少为1,接到连接请求后,这些请求必须排队,如果队列已满,则拒绝请求。
4.服务器套接字通过socket的accept方法等待客户请求一个连接:
connection,address=socket.accept()
调用accept方法时,socket会进入'waiting'(或阻塞)状态。客户请求连接时,方法建立连接并返回服务器。accept方法返回 一个含有俩个元素的元组,形如(connection,address)。第一个元素(connection)是新的socket对象,服务器通过它与客 户通信;第二个元素(address)是客户的internet地址。
5. 处理阶段,服务器和客户通过send和recv方法通信(传输数据)。服务器调用send,并采用字符串形式向客户发送信息。send方法 返回已发送的字符个数。服务器使用recv方法从客户接受信息。调用recv时,必须指定一个整数来控制本次调用所接受的最大数据量。recv方法在接受 数据时会进入'blocket'状态,最后返回一个字符串,用它来表示收到的数据。如果发送的量超过recv所允许,数据会被截断。多余的数据将缓冲于接 受端。以后调用recv时,多余的数据会从缓冲区删除。
6. 传输结束,服务器调用socket的close方法以关闭连接。
建立一个简单客户连接则需要4个步骤。
第1步,创建一个socket以连接服务器 socket=socket.socket(family,type)
第2步,使用socket的connect方法连接服务器 socket.connect((host,port))
第3步,客户和服务器通过send和recv方法通信。
第4步,结束后,客户通过调用socket的close方法来关闭连接。
以下案例都是以TCP方式连接
import socket sk = socket.socket() address = ('127.0.0.1',8000) sk.bind(address) sk.listen(2) print("......") while True: conn, addr = sk.accept() print(addr) while True: try: date = conn.recv(1024) except Exception: break if not date:break print(str(date, "utf8")) inp = input(">>>>:") conn.send(bytes(inp,"utf8")) # conn, addr = sk.accept() # while True: # date = conn.recv(1024) # if not date: # conn, addr = sk.accept() # continue # print(str(date, "utf8")) # inp = input(">>>>:") # conn.send(bytes(inp,"utf8")) # conn.close() conn.close()
import socket sk = socket.socket() address = ("127.0.0.1",8000) sk.connect(address) while True: inp = input(">>>>:") if inp == "q": break sk.send(bytes(inp, "utf8")) date = sk.recv(1024) print(str(date,"utf8")) sk.close()
简单的模拟qq对话(socket模块)
import socket,subprocess sk = socket.socket() address = ('0.0.0.0',8000) sk.bind(address) sk.listen(2) print("......") while True: conn, addr = sk.accept() print(addr) while True: try: date = conn.recv(1024) except Exception: break if not date:break print(str(date, "utf8")) obj = subprocess.Popen(str(date, "utf8"),shell=True,stdout=subprocess.PIPE) cmd_result = obj.stdout.read() result_len = str(len(cmd_result)) print(result_len) conn.send(bytes(result_len,"utf8")) conn.send(cmd_result) conn.close()