python网络编程理解

TCP/IP简介

虽然大家现在对互联网很熟悉,但是计算机网络的出现比互联网要早很多。
计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM、Apple和Microsoft都有各自的网络协议,互不兼容,这就好比一群人有的说英语,有的说中文,有的说德语,说同一种语言的人可以交流,不同的语言之间就不行了。
为了把全世界的所有不同类型的计算机都连接起来,就必须规定一套全球通用的协议,为了实现互联网这个目标,互联网协议簇(Internet Protocol Suite)就是通用协议标准。Internet是由inter和net两个单词组合起来的,原意就是连接“网络”的网络,有了Internet,任何私有网络,只要支持这个协议,就可以联入互联网。
因为互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议。
通信的时候,双方必须知道对方的标识,好比发邮件必须知道对方的邮件地址。互联网上每个计算机的唯一标识就是IP地址,类似123.123.123.123。如果一台计算机同时接入到两个或更多的网络,比如路由器,它就会有两个或多个IP地址,所以,IP地址对应的实际上是计算机的网络接口,通常是网卡。
IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因此,路由器就负责决定如何把一个IP包转发出去。IP包的特点是按块发送,途径多个路由,但不保证能到达,也不保证顺序到达。

IP地址实际上是一个32位整数(称为IPv4),以字符串表示的IP地址如192.168.0.1实际上是把32位整数按8位分组后的数字表示,目的是便于阅读。
IPv6地址实际上是一个128位整数,它是目前使用的IPv4的升级版,以字符串表示类似于2001:0db8:85a3:0042:1000:8a2e:0370:7334。
TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。
许多常用的更高级的协议都是建立在TCP协议基础上的,比如用于浏览器的HTTP协议、发送邮件的SMTP协议等。
一个TCP报文除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。
端口有什么作用?在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。一个TCP报文来了之后,到底是交给浏览器还是QQ,就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。
一个进程也可能同时与多个计算机建立链接,因此它会申请很多端口。
了解了TCP/IP协议的基本概念,IP地址和端口的概念,我们就可以开始进行网络编程了。

TCP编程

Socket是网络编程的一个抽象概念。通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。

客户端

大多数连接都是可靠的TCP连接。创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器。
举个例子,当我们在浏览器中访问新浪时,我们自己的计算机就是客户端,浏览器会主动向新浪的服务器发起连接。如果一切顺利,新浪的服务器接受了我们的连接,一个TCP连接就建立起来的,后面的通信就是发送网页内容了。
所以,我们要创建一个基于TCP连接的Socket,可以这样做:
导入socket库:
import socket

创建一个socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
建立连接:
s.connect((‘www.sina.com.cn‘, 80))
创建Socket时,AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6。SOCK_STREAM指定使用面向流的TCP协议,这样,一个Socket对象就创建成功,但是还没有建立连接。
客户端要主动发起TCP连接,必须知道服务器的IP地址和端口号。新浪网站的IP地址可以用域名www.sina.com.cn自动转换到IP地址,但是怎么知道新浪服务器的端口号呢?
答案是作为服务器,提供什么样的服务,端口号就必须固定下来。由于我们想要访问网页,因此新浪提供网页服务的服务器必须把端口号固定在80端口,因为80端口是Web服务的标准端口。其他服务都有对应的标准端口号,例如SMTP服务是25端口,FTP服务是21端口,等等。端口号小于1024的是Internet标准服务的端口,端口号大于1024的,可以任意使用。
因此,我们连接新浪服务器的代码如下:
s.connect((‘www.sina.com.cn‘, 80))
注意参数是一个tuple,包含地址和端口号。
建立TCP连接后,我们就可以向新浪服务器发送请求,要求返回首页的内容:
发送数据:
s.send(b‘GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n‘)
TCP连接创建的是双向通道,双方都可以同时给对方发数据。但是谁先发谁后发,怎么协调,要根据具体的协议来决定。例如,HTTP协议规定客户端必须先发请求给服务器,服务器收到后才发数据给客户端。
发送的文本格式必须符合HTTP标准,如果格式没问题,接下来就可以接收新浪服务器返回的数据了:
接收数据:
buffer = []
while True:
每次最多接收1k字节:
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b‘‘.join(buffer)
接收数据时,调用recv(max)方法,一次最多接收指定的字节数,因此,在一个while循环中反复接收,直到recv()返回空数据,表示接收完毕,退出循环。
当我们接收完数据后,调用close()方法关闭Socket,这样,一次完整的网络通信就结束了:
关闭连接:
s.close()
接收到的数据包括HTTP头和网页本身,我们只需要把HTTP头和网页分离一下,把HTTP头打印出来,网页内容保存到文件:
header, html = data.split(b‘\r\n\r\n‘, 1)
print(header.decode(‘utf-8‘))
把接收的数据写入文件:
with open(‘sina.html‘, ‘wb‘) as f:
f.write(html)
现在,只需要在浏览器中打开这个sina.html文件,就可以看到新浪的首页了。
客户编程编程
1.创建Socket;2.连接到远程服务器;3.发送数据;4.接收数据;5.关闭socket
当我们打开www.baidu.com时,浏览器所做的就是以上5个步骤,在Socket中具有这种行为特征的被称为Client,客户端主要是连接远程系统并获取数据;(Socket中另一种行为称为Server,服务器使用Socket来接收连接以及提供数据,和客户端正好相反,所以,www.baidu.com是HTTP服务器,浏览器是HTTP客户端)
服务器端程序的编写如下:

服务器

和客户端编程相比,服务器编程就要复杂一些。
服务器进程首先要绑定一个端口并监听来自其他客户端的连接。如果某个客户端连接过来了,服务器就与该客户端建立Socket连接,随后的通信就靠这个Socket连接了。
所以,服务器会打开固定端口(比如80)监听,每来一个客户端连接,就创建该Socket连接。由于服务器会有大量来自客户端的连接,所以,服务器要能够区分一个Socket连接是和哪个客户端绑定的。一个Socket依赖4项:服务器地址、服务器端口、客户端地址、客户端端口来唯一确定一个Socket。
但是服务器还需要同时响应多个客户端的请求,所以,每个连接都需要一个新的进程或者新的线程来处理,否则,服务器一次就只能服务一个客户端了。
我们来编写一个简单的服务器程序,它接收客户端连接,把客户端发过来的字符串加上Hello再发回去。
首先,创建一个基于IPv4和TCP协议的Socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
然后,我们要绑定监听的地址和端口。服务器可能有多块网卡,可以绑定到某一块网卡的IP地址上,也可以用0.0.0.0绑定到所有的网络地址,还可以用127.0.0.1绑定到本机地址。127.0.0.1是一个特殊的IP地址,表示本机地址,如果绑定到这个地址,客户端必须同时在本机运行才能连接,也就是说,外部的计算机无法连接进来。
端口号需要预先指定。因为我们写的这个服务不是标准服务,所以用9999这个端口号。请注意,小于1024的端口号必须要有管理员权限才能绑定:
监听端口:
s.bind((‘127.0.0.1‘, 9999))
紧接着,调用listen()方法开始监听端口,传入的参数指定等待连接的最大数量:
s.listen(5)
print(‘Waiting for connection...‘)
接下来,服务器程序通过一个永久循环来接受来自客户端的连接,accept()会等待并返回一个客户端的连接:
while True:
接受一个新连接:
sock, addr = s.accept()
创建新线程来处理TCP连接:
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
每个连接都必须创建新线程(或进程)来处理,否则,单线程在处理连接的过程中,无法接受其他客户端的连接:
def tcplink(sock, addr):
print(‘Accept new connection from %s:%s...‘ % addr)
sock.send(b‘Welcome!‘)
while True:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode(‘utf-8‘) == ‘exit‘:
break
sock.send((‘Hello, %s!‘ % data.decode(‘utf-8‘)).encode(‘utf-8‘))
sock.close()
print(‘Connection from %s:%s closed.‘ % addr)
连接建立后,服务器首先发一条欢迎消息,然后等待客户端数据,并加上Hello再发送给客户端。如果客户端发送了exit字符串,就直接关闭连接。
要测试这个服务器程序,我们还需要编写一个客户端程序:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
建立连接:
s.connect((‘127.0.0.1‘, 9999))
接收欢迎消息:
print(s.recv(1024).decode(‘utf-8‘))
发送数据:
s.send(data)
print(s.recv(1024).decode(‘utf-8‘))
s.send(b‘exit‘)
s.close()
服务器编程:
1.打开socket;2.绑定到特定的地址以及端口上;3.监听连接;4.建立连接;5.接收/发送数据 
绑定Socekt:函数bind可以用来将socket绑定到特定的地址和端口上,绑定完成后,可以开启监听模式
监听连接listen:将socket置于监听模式,该函数带有一个参数backlog,用来控制连接的个数,如果设为10,那么有10个连接正在等待处理,此时,第11个请求过来时,将会被拒绝。
接收连接:当有客户端向服务器发送连接请求时,服务器会接收连接
客户端程序的编写如下:

accept函数的理解:
accept返回的socket(记为socket1)是一个不同于socket创建的socket(记为socket2),socket2是用来监听和建立连接的,不能进行数据的发送与接收(所以这个socket不需要客户端的信息,它就是用于监听连接的),socket1是专门用于发送接收数据的,他记录的信息是所连接的客户端socket的信息,记录的是客户端的协议地址,addr就是客户端的IP地址,端口号元组,这个端口号是由客户端来确定的,之所以会这样理解是因为,客户端自身肯定知道自身的IP,端口,要进行通信,肯定只需要对方的IP,端口好,同样服务器端要发送接收数据,也要知道客户端的协议地址端口号才行,如果记录的是自身的信息,就没法与客户端通信了。

UDP编程

TCP是建立可靠连接,并且通信双方都可以以流的形式发送数据。相对TCP,UDP则是面向无连接的协议。
使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。
虽然用UDP传输数据不可靠,但它的优点是和TCP比,速度快,对于不要求可靠到达的数据,就可以使用UDP协议。
我们来看看如何通过UDP协议传输数据。和TCP类似,使用UDP的通信双方也分为客户端和服务器。服务器首先需要绑定端口:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
绑定端口:
s.bind((‘127.0.0.1‘, 9999))
创建Socket时,SOCK_DGRAM指定了这个Socket的类型是UDP。绑定端口和TCP一样,但是不需要调用listen()方法,而是直接接收来自任何客户端的数据:
print(‘Bind UDP on 9999...‘)
while True:
接收数据:
data, addr = s.recvfrom(1024)
print(‘Received from %s:%s.‘ % addr)
s.sendto(b‘Hello, %s!‘ % data, addr)
recvfrom()方法返回数据和客户端的地址与端口,这样,服务器收到数据后,直接调用sendto()就可以把数据用UDP发给客户端。
注意这里省掉了多线程,因为这个例子很简单。
客户端使用UDP时,首先仍然创建基于UDP的Socket,然后,不需要调用connect(),直接通过sendto()给服务器发数据:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b‘Michael‘, b‘Tracy‘, b‘Sarah‘]:
发送数据:
s.sendto(data, (‘127.0.0.1‘, 9999))
接收数据:
print(s.recv(1024).decode(‘utf-8‘))
s.close()
从服务器接收数据仍然调用recv()方法。
客户端编程代码


服务器端编程代码


socket参数理解
accept函数的理解
listten函数的理解
socket的理解

原文地址:https://www.cnblogs.com/wxhwsr/p/10580847.html

时间: 2024-10-08 00:16:39

python网络编程理解的相关文章

[python] 网络编程之套接字Socket、TCP和UDP通信实例

很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send.Receive方法的同步通讯 Python网络编程也类似.同时最近找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. 最后希望文章对你有所帮助,如果有不

python 网络编程:socket

在学习socket之前,我们先复习下相关的网络知识. OSI七层模型:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层.OSI七层模型是由国际标准化组织ISO定义的网络的基本结构,不仅包括一些概念和结构,还包括一系列的协议. TCP/IP四层模型:既然有OSI七层模型,为什么我们还要定义一个TCP/IP的四层模型呢,那是因为OSI七层模型对应面过于宽泛,很多概念实现不了,也没必要实现,因此,实际生产中广泛应用的是TCP/IP四层结构,他们的对应关系如下表: TCP/IP OSI 应用层

python 网络编程:socket和select实现伪并发

上节地址:Python网络编程:socket 先补充点内容: 一.send和sendall区别 send,sendall ret = send('safagsgdsegsdgew') #send 发送完成后会有一个返回值,告知发送了多少,并不一定会把数据全部发送过去. sendall:内部调用send,将数据全部发送完为止. 因此我们使用时最好使用sendall 二.粘包 粘包问题需要理解recv()的使用,我们定义接收值的时候会写recv(1024)表示一次接收1024字节,但是有时候接收的数

python网络编程基础(线程与进程、并行与并发、同步与异步)

python网络编程基础(线程与进程.并行与并发.同步与异步) 目录 线程与进程 并行与并发 同步与异步 线程与进程 进程 前言 进程的出现是为了更好的利用CPU资源使到并发成为可能. 假设有两个任务A和B,当A遇到IO操作,CPU默默的等待任务A读取完操作再去执行任务B,这样无疑是对CPU资源的极大的浪费.聪明的老大们就在想若在任务A读取数据时,让任务B执行,当任务A读取完数据后,再切换到任务A执行.注意关键字切换,自然是切换,那么这就涉及到了状态的保存,状态的恢复,加上任务A与任务B所需要的

python 网络编程 (二)---tcp

异常 python的socket模块实际上定义了4种可能出现的异常: 1)与一般I/O 和通信问题有关的socket.error; 2)与查询地址信息有关的socket.gaierror; 3)与其他地址错误有关的socket.herror; 4)与在一个socket上调用settimeout()后,处理超时有关的socket.timeout; import socket, sys, time host = sys.argv[1] textport = sys.argv[2] filename

[Python网络编程] DNS缓存解决方案

记得以前写爬虫的时候为了防止dns多次查询,是直接修改/etc/hosts文件的,最近看到一个优美的解决方案,修改后记录如下: import socket _dnscache={} def _setDNSCache(): """ Makes a cached version of socket._getaddrinfo to avoid subsequent DNS requests. """ def _getaddrinfo(*args, **

Python 网络编程

Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法. 高级别的网络服务模块 SocketServer, 它提供了服务器中心类,可以简化网络服务器的开发. 什么是 Socket? Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯. socket()函数 Pyt

[Python网络编程]gevent httpclient以及网页编码

之前看到geventhttpclient这个项目,https://github.com/gwik/geventhttpclient,官方文档说非常快,由于响应使用了C的解析,所以我一直想把这玩意用到项目中, 这两天一直在纠结这玩意,说实在一句话,比较难用,封装的不给力,最大缺陷如下: 1.不支持重定向,重定向需要自己来写,很费事 2.新建的httpclient对象只能发送同域名的请求 这相当的蛋疼,我花了一点时间封装了一下,解决了上面的两个问题,还增加了自动编解码问题,代码如下: #!/usr/

python 网络编程(五)---DNS域名系统

1.域名系统定义 DNS计算机域名系统由域名服务器和域名解析器组成.通常输入的是网址就是一个域名. 2.域名查询 查询方式包括: 1)正向查询:由域名查找对应的IP(如:119.75.218.77">www.baidu.com->119.75.218.77 ) 2)反向查询:由IP查找域名(如:119.75.218.77 –> www.baidu.com) 查询方式包括: 1)递归查询:当DNS服务器接收到客户端的查询请求时,会做出相应的反应(本地DNS服务器查询.其他服务器查