一、前言
Socket是计算机之间进行网络通信的一套程序接口,最初由Berkeley大学研发,目前已经成为网络编程的标准,可以实现跨平台的数据传输。Socket是网络通信的基础,相当于在发送端和接收端建立了一个管道来实现数据和命令的相互传递。python提供了socket模块,对Socket进行了二次封装,支持Socket接口的访问,大幅度简化了程序的开发步骤,提高了开发效率。除此之外,python还提供了urllib等大量模块可以对网页内容进行读取和处理,在此基础上结合多线程编程以及其他有关模块可以快速开发网页爬虫之类的应用。可以使用python语言编写CGI程序,也可以把python代码嵌入到网页中运行,而借助于web2py,Flash、Django框架,则可以快速开发网站应用。
二、计算机网络基础
1、网络体系结构。
目前较为主流的网络体系结构是ISO/OSI参考模型和TCP/IP协议族。这两种体系结构都采用了分层设计和实现的方式。例如,ISO/OSI参考模型从上而下划分为应用层、表示层、会话层、传输层、网络层、数据链路层和物理层,而TCP/IP则将网络划分为应用层、传输层、网络层和数据链路层。分层设计的好处是,各层之间可以独立设计和实现,只要保证相邻层之间的调用规范和接口不变,就可以方便、灵活第改变某层的内部实现以进行优化或完成其他要求。以下是OSI模型的各层划分:
(1)OSI模型各层运行常见的物理设备:
(2)OSI模型各层常见的协议
(3)理解socket层。Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
其实站在我们的角度上看,socket就是一个模块。我们通过调用模块中已经实现的方法建立两个进程之间的连接和通信。 也有人将socket说成ip+port,因为ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序。 所以我们只要确立了ip和port就能找到一个应用程序,并且使用socket模块来与之通信。
2、网络协议。
网络协议是计算机网络中进行数据交换而建立的规则、标准或约定的集合。网络协议的三要素分别为语法、语义和时序。简单的讲,可以这么理解,语义表示要做什么,语法表示要怎么做,时序规定了各种事件出现的顺序。
(1)语法。语法规定了用户数据与控制信息的结构和格式。
(2)语义。语义用来解释控制信息么个部分的意义,规定了需要发出何种控制信息,以及需要完成的动作和做出什么样的响应。
(3)时序。时序是对事件发生顺序的详细说明,也可称为“同步”。
3、应用层协议。
应用层协议直接与最终用户进行交互,定义了运行在不同终端系统上的应用程序进程如何互相传递报文。下面简单列出几种常见的应用层协议。
(1)DNS。域名服务器,用来实现域名与IP地址的转换。
(2)FTP。文件传输协议,可以通过网络在不同平台之间实现文件的传输。
(3)HTTP。超文本传输协议。
(4)SMTP。简单邮件传输协议。
(5)TELNET。远程登录协议。
4、传输层协议。
在传输层主要运行着TCP和UDP两个协议,其中TCP是面向连接的,具有质量保证的可靠传输协议,但开销较大;UDP是尽最大能力传输的无连接协议,开销小,但不可靠。常用于视频在线点播(Video On Demand,VOD)之类的应用。TCP和UDP并没有优劣之分,仅仅是适用场景有所不同。在传输层,使用端口号来标识和区分具体的应用层进程,每当创建一个应用层网络进程时系统就会自动分配一个端口号与之关联,是实现网络基础上端到端通信的重要基础。
当应用程序希望通过 TCP 与另一个应用程序通信时,它会发送一个通信请求。这个请求必须被送到一个确切的地址。在双方“握手”之后,TCP 将在两个应用程序之间建立一个全双工 (full-duplex) 的通信。这个全双工的通信将占用两个计算机之间的通信线路,直到它被一方或双方关闭为止。
TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK[1],并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接。[1] TCP三次握手的过程如下: 客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。 三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。
建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的半关闭(half-close)造成的。 (1) 某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕。 (2) 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认。 注意:FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。 (3) 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。 (4) 接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。[1] 既然每个方向都需要一个FIN和一个ACK,因此通常需要4个分节。 注意: (1) “通常”是指,某些情况下,步骤1的FIN随数据一起发送,另外,步骤2和步骤3发送的分节都出自执行被动关闭那一端,有可能被合并成一个分节。[2] (2) 在步骤2与步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为“半关闭”(half-close)。 (3) 当一个Unix进程无论自愿地(调用exit或从main函数返回)还是非自愿地(收到一个终止本进程的信号)终止时,所有打开的描述符都被关闭,这也导致仍然打开的任何TCP连接上也发出一个FIN。 无论是客户还是服务器,任何一端都可以执行主动关闭。通常情况是,客户执行主动关闭,但是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。[2]
现在Internet上流行的协议是TCP/IP协议,该协议中对低于1024的端口都有确切的定义,他们对应着Internet上一些常见的服务。这些常见的服务可以分为使用TCP端口(面向连接)和使用UDP端口(面向无连接)两种。
说到TCP和UDP,首先要明白“连接”和“无连接”的含义,他们的关系可以用一个形象地比喻来说明,就是打电话和写信。两个人如果要通话,首先要建立连接——即打电话时的拨号,等待响应后——即接听电话后,才能相互传递信息,最后还要断开连接——即挂电话。写信就比较简单了,填写好收信人的地址后将信投入邮筒,收信人就可以收到了。从这个分析可以看出,建立连接可以在需要痛心地双方建立一个传递信息的通道,在发送方发送请求连接信息接收方响应后,由于是在接受方响应后才开始传递信息,而且是在一个通道中传送,因此接受方能比较完整地收到发送方发出的信息,即信息传递的可靠性比较高。但也正因为需要建立连接,使资源开销加大(在建立连接前必须等待接受方响应,传输信息过程中必须确认信息是否传到及断开连接时发出相应的信号等),独占一个通道,在断开连接钱不能建立另一个连接,即两人在通话过程中第三方不能打入电话。而无连接是一开始就发送信息(严格说来,这是没有开始、结束的),只是一次性的传递,是先不需要接受方的响应,因而在一定程度上也无法保证信息传递的可靠性了,就像写信一样,我们只是将信寄出去,却不能保证收信人一定可以收到。
TCP是面向连接的,有比较高的可靠性, 一些要求比较高的服务一般使用这个协议,如FTP、Telnet、SMTP、HTTP、POP3等。
而UDP是面向无连接的,使用这个协议的常见服务有DNS、SNMP、QQ等。对于QQ必须另外说明一下,QQ2003以前是只使用UDP协议的,其服务器使用8000端口,侦听是否有信息传来,客户端使用4000端口,向外发送信息(这也就不难理解在一般的显IP的QQ版本中显示好友的IP地址信息中端口常为4000或其后续端口的原因了),即QQ程序既接受服务又提供服务,在以后的QQ版本中也支持使用TCP协议了。
5、IP地址。
IP运行于网络体系接口的网络层,是网络互连的重要基础。IP地址(32位或128位二进制数)用来标识网络上的主机,在公开网络上或同一个局域网内部,每台主机都必须使用不同的IP地址;而由于网络转换(Network Address Translation,NAT)和代理服务器等技术的广泛应用,不同内网之间的主机IP地址可以相同并且可以互不影响地正常工作。IP地址与端口号共同用来标识网络上特定主机的特定应用进程,俗称Socket。
6、MAC地址与arp协议。
MAC地址也称为网卡地址或物理地址,是一个48位的二进制数,用来标识不同的网卡物理地址。本机的IP地址和MAC地址可以在命令提示符窗口中使用ipconfig/all命令查看,如下图所示:
arp协议。地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到网络上的所有主机,并接收返回消息,以此确定目标的物理地址。收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。地址解析协议是建立在网络中各个主机互相信任的基础上的,网络上的主机可以自主发送ARP应答消息,其他主机收到应答报文时不会检测该报文的真实性就会将其记入本机ARP缓存;由此攻击者就可以向某一主机发送伪ARP应答报文,使其发送的信息无法到达预期的主机或到达错误的主机,这就构成了一个ARP欺骗。ARP命令可用于查询本机ARP缓存中IP地址和MAC地址的对应关系、添加或删除静态对应关系等。相关协议有RARP、代理ARP。NDP用于在IPv6中代替地址解析协议。
三、软件开发的架构
我们了解的涉及到两个程序之间通讯的应用大致可以分为两种:
第一种是应用类:qq、微信、网盘、优酷这一类是属于需要安装的桌面应用
第二种是web类:比如百度、知乎、博客园等使用浏览器访问就可以直接使用的应用
以上两种应用的本质其实都是两个程序之间的通讯。而这两个分类又对应了两个软件开发的架构。
1、C/S架构
C/S即:Client与Server ,中文意思:客户端与服务器端架构,这种架构也是从用户层面(也可以是物理层面)来划分的。这里的客户端一般泛指客户端应用程序EXE,程序需要先安装后,才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大。
2、B/S架构
B/S即:Browser与Server,中文意思:浏览器端与服务器端架构,这种架构是从用户层面来划分的。Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。
四、(套接字,socket)的初使用
python中socket的参数详解??:
from socket import * sk = socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None) print(sk) 运行结果: <socket.socket fd=460, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
family:地址系列应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。 (AF_UNIX 域实际上是使用本地 socket 文件来通信) type:套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。 SOCK_STREAM 是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送。 SOCK_DGRAM 是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播信息。 proto:协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。 fileno:如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的。这可能有助于使用socket.close()关闭一个独立的插座。
1、基于tcp协议的socket。tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端。
import socket sk = socket.socket() sk.bind((‘127.0.0.1‘,8888)) #把地址绑定到套接字 sk.listen() #监听链接 conn,addr = sk.accept() #接受客户端链接 ret = conn.recv(1024) #接收客户端信息 print(ret) #打印客户端信息 conn.send(b‘hello client!‘) #向客户端发送信息 conn.close() #关闭客户端套接字 sk.close() #关闭服务器套接字(可选)
import socket sk = socket.socket() # 创建客户套接字 sk.connect((‘127.0.0.1‘,8888)) # 尝试连接服务器 sk.send(b‘hello server!‘) ret = sk.recv(1024) # 对话(发送/接收) print(ret) sk.close() # 关闭客户套接字
2、基于udp协议的socket。udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接。
import socket udp_sk = socket.socket(type=socket.SOCK_DGRAM) #创建一个服务器的套接字 udp_sk.bind((‘127.0.0.1‘,8889)) #绑定服务器套接字 msg,addr = udp_sk.recvfrom(1024) print(msg) udp_sk.sendto(b‘hello client!‘,addr) # 对话(接收与发送) udp_sk.close() # 关闭服务器套接字
import socket ip_port=(‘127.0.0.1‘,8889) udp_sk=socket.socket(type=socket.SOCK_DGRAM) udp_sk.sendto(b‘hello server!‘,ip_port) back_msg,addr=udp_sk.recvfrom(1024) print(back_msg.decode(‘utf-8‘),addr)
3.基于udp协议的socket来实现qq聊天。
mport socket ip_port=(‘127.0.0.1‘,8081) u_sk=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) u_sk.bind(ip_port) while True: qq_msg,addr=u_sk.recvfrom(1024) print(‘来自[%s:%s]的一条消息:\033[1;42m%s\033[0m‘ %(addr[0],addr[1],qq_msg.decode(‘utf-8‘))) back_msg=input(‘回复消息: ‘).strip() u_sk.sendto(back_msg.encode(‘utf-8‘),addr)
import socket bufsize = 1024 u_sk=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) qq_name_dic={ ‘罗盛运‘:(‘127.0.0.1‘,8081), ‘罗德华‘:(‘127.0.0.1‘,8081), ‘刘永贵‘:(‘127.0.0.1‘,8081), ‘周曾吕‘:(‘127.0.0.1‘,8081), } while True: qq_name=input(‘请选择聊天对象: ‘).strip() while True: msg=input(‘请输入消息,回车发送,输入q结束和他的聊天: ‘).strip() if msg == ‘q‘:break if not msg or not qq_name or qq_name not in qq_name_dic:continue u_sk.sendto(msg.encode(‘utf-8‘),qq_name_dic[qq_name]) back_msg,addr=u_sk.recvfrom(bufsize) print(addr) print(‘来自[%s:%s]的一条消息:\033[1;41m%s\033[0m‘ %(addr[0],addr[1],back_msg.decode(‘utf-8‘))) u_sk.close()
4.时间服务器
from socket import * from time import strftime ip_port = (‘127.0.0.1‘, 9888) bufsize = 1024 tcp_server = socket(AF_INET, SOCK_DGRAM) tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) tcp_server.bind(ip_port) while True: msg, addr = tcp_server.recvfrom(bufsize) print(‘===>‘, msg) if not msg: time_fmt = ‘%Y-%m-%d %X‘ else: time_fmt = msg.decode(‘utf-8‘) back_msg = strftime(time_fmt) tcp_server.sendto(back_msg.encode(‘utf-8‘), addr) tcp_server.close()
from socket import * ip_port=(‘127.0.0.1‘,9888) bufsize=1024 tcp_client=socket(AF_INET,SOCK_DGRAM) while True: msg=input(‘请输入时间格式(例%Y %m %d)>>: ‘).strip() tcp_client.sendto(msg.encode(‘utf-8‘),ip_port) data=tcp_client.recv(bufsize) print(data)
原文:https://www.cnblogs.com/lcs-LLH/p/10890192.html