[python] socket模块(TCP/IP网络编程)
本文只关心python的网络编程,并重点学习TCP/IP单元。
一、简单了解下:
1、什么是TCP/IP、UDP?
TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
这里有一张图,表明了这些协议的关系。
TCP/IP协议族包括运输层(传输层)、网络层、(数据)链路层。现在你知道TCP/IP与UDP的关系了吧。
2、Socket在哪里呢?
在上图中,我们没有看到Socket的影子,那么它到底在哪里呢?还是用图来说话,一目了然。
原来Socket在这里。
3、Socket是什么呢?
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
4、怎样使用socket?
前人已经给我们做了好多的事了,网络间的通信也就简单了许多,但毕竟还是有挺多工作要做的。以前听到Socket编程,觉得它是比较高深的编程知识,但是只要弄清Socket编程的工作原理,神秘的面纱也就揭开了。
一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。 生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
二、socket模块函数
由于socket模块中的属性太多,可直接使用import socket ,这样能大幅缩减代码。
1、socket(family,type)
创建套接字。
套接字是计算机网络数据结构。网络化的应用程序在开始任何通讯之前都必须创建套接字。可以比作电话的插口一样,没有它就完全没办法通信。
创建TCP/IP套接字:
1 tcpsock = socket(AF_INET , SOCK_STREAM)
创建UDP/IP套接字:
1 udpsock = socket(AF_INET , SOCK_DGRAM)
AF:地址家族的缩写
SOCK_STREAM:面向连接套接字,即在通信之前一定要简历一条连接(就像跟朋友打电话)。面向连接的通信方式提供了顺序的、可靠的、不会重复的数据传输,而且也不会被加上数据边界。发送信息时,信息能被拆分成多份,没分都会到达正确的目的地,然后被重新按顺序拼装起来,传给正在等待的应用程序。
SOCK_DGRAM:无连接套接字,即无需建立连接就可以进行通讯。但此时,数据到达是顺序、可靠性及不重复性就无法保证了,数据报会保留数据边界。数据是整个发送的。使用数据报来传输数据就像快递包裹一样,不一定会按发送顺序到达,而且还有可能根本到达不了。
2、套接字对象(内建)方法
函数 | 描述 |
服务器端套接字函数 | |
s.bind() |
绑定地址(主机名,端口号)到套接字 合法端口号范围为0~65535。其中小于1024的为系统保留端口,其余的均可用 |
s.listen() | 开始TCP监听。参数表示最多允许多少个连接同时连进来,而后边的连接就会被拒绝掉 |
s.accept() | (阻塞式)接受连接并返回(conn,address),conn是新的套接字对象,可以用来通过连接发送和接收数据,address是另一个连接端的套接字地址 |
客户端套接字函数 | |
s.connect() | 连接到address处的远程套接字。有错误时出现socket.error |
s.connect_ex() | connect()的扩展版本,出错时返回出错码,而不是抛出异常。成功返回0 |
公共用途的套接字函数 | |
s.recv(bufsize [,flags]) | 接收TCP数据。bufsize指定要接收的最大数据量。flags提供有关消息的其他信息,通常忽略 |
s.send() | 发送TCP数据 |
s.sendall() | 完整发送TCP数据 |
s.recvfrom(bufsize [,flags]) | 接收UDP数据。返回(data,address),data是接收数据字符串,address是发送数据的套接字地址 |
s.sendto(string [,flags],address) | 发送UDP数据。address形式是元组,指定远程地址 |
s.fileno() | 返回套接字的文件描述符 |
s.makefile(mode[,bufsize]) | 创建与套接字关联的文件对象。mode中'r'表示读取,'w'表示写入,'a'表示添加,'+'表示读写,'b'表示二进制访问。 |
3、异常
error:该异常表示与套接字或者地址有关的错误。它返回一个(errno,mesg)对以及底层系统调用返回的错误。继承自IOError。
herror:表示与地址有关的错误。返回一个包含错误编号的错误消息的元祖(herrno,hmesg)。继承自error。
4、创建TCP连接实例代码
客户端
1 import socket 2 3 HOST = '192.168.1.100' #服务器IP 4 PORT = 12345 #端口号 5 BUFSIZ = 1024 6 ADDR = (HOST,PORT) 7 8 tcpCliSock = socket(AF_INET,SOCK_STREAM) #创建TCP/IP套接字 9 tcpCliSock.connect(ADDR) #尝试连接服务器 10 11 while True: 12 data = input('> ') 13 if not data: 14 break 15 tcpCliSock.send(data) #发送TCP数据 16 data = tcpCliSock.recv(BUFSIZ) #接收TCP数据 17 if not data: 18 break 19 print(data) 20 21 tcpCliSock.close
服务器
1 import socket 2 import time 3 4 HOST = '192.168.1.100' 5 PORT = 12345 6 BUFSIZE = 1024 7 ADDR = (HOST,PORT) 8 9 tcpSerSock = socket(AF_INET,SOCK_STREAM) 10 tcpSerSock.bind(ADDR) 11 tcpSerSock.linsten(5) 12 13 while True: 14 print('waiting for connection... ') 15 tcpCliSock,addr = tcpSerSock.accept() 16 print('...connected from : ',addr) 17 18 while True: 19 data = tcpCliSock.recv(BUFSIZE) 20 if not data: 21 break 22 tcpSerSock.send((' [%s] %s' % (ctime(),data))) 23 24 tcpCliSock.close() 25 tcpSerSock.close()
代码实现:
当有连接时,进入对话循环,等待客户端发送数据。如果消息为空,表示客户端已经退出,再等待下一个客户端连接。得到客户端消息后,在消息前叫一个时间戳然后返回。
要先开服务器,后开客户端。
5、创建UDP连接实例代码
客户端
1 import socket 2 3 HOST = '192.168.1.100' #服务器IP 4 PORT = 12345 #端口号 5 BUFSIZ = 1024 6 ADDR = (HOST,PORT) 7 udpCliSock = socket(AF_INET,SOCK_DGRAM) 8 9 while True: 10 data = input('> ') 11 if not data: 12 break 13 udpCliSock.sendto(data,ADDR) #发送UDP数据 14 data,ADDR = udpCliSock.recvfrom(BUFSIZ) #接收UDP数据 15 if not data: 16 break 17 print(data) 18 udpCliSock.close() 19 udpCliSock.close
服务器
1 import socket 2 import time 3 4 HOST = '192.168.1.100' #服务器IP 5 PORT = 12345 #端口号 6 BUFSIZ = 1024 7 ADDR = (HOST,PORT) 8 9 udpSerSock = socket(AF_INET,SOCK_DGRAM) 10 udpSerSock.bind(ADDR) 11 12 while True: 13 print('waiting for message... ') 14 data,addr = udpSerSock.recvfrom(BUFSIZ) 15 udpSerSock.sendto('[%s] %s' % (ctime(,data),addr)) 16 print(' ...received from and returned to:',addr) 17 udpCliSock.close
UDP和TCP服务器的一个重要区别就是,由于数据报套接字是无连接的,所以无法把客户端连接交给另外的套接字进行后续的通讯。这些服务器知识接受消息,需要的话,给客户端返回一个结果就可以了。
服务器输出客户端信息的原因是,服务器可能会得到并回复多个客户端消息,这时,输出就可以了解系哦啊系来自那里。对于TCP服务器来说,由于客户端会创建一个连接,我们自然就知道消息来自那里了。