【Python入门】39.网络编程

摘要:网络编程的概念介绍;TCP/IP协议的基本介绍;介绍Python的socket库,在Python中进行TCP编程与UDP编程。


*写在前面:为了更好的学习python,博主记录下自己的学习路程。本学习笔记基于廖雪峰的Python教程,如有侵权,请告知删除。欢迎与博主一起学习Pythonヽ( ̄▽ ̄)ノ *


目录

网络编程
TCP/IP协议
TCP编程
客户端
服务器
UDP编程

网络编程

计算机网络就是把各个计算机连接在一起,让各个计算机之间可以通信。

网络编程就是要通过程序编程来实现两个计算机之间的通信。

实际上,网络通信就是两个进程之间的通信

比如我们用浏览器访问百度网页,这一过程就是浏览器进程与百度服务器上的某个Web服务进程在通信。

网络编程对所有开发语言都是一样的。接下来将简单介绍TCP/IP协议,然后介绍如何在Python中进行TCP编程与UDP编程。

TCP/IP协议

早期的计算机网络,为了实现计算机之间的联网,各厂商自己规定自己的协议,从而实现联网。但是使用不同网络协议的计算机之间是不可以联网的。

后来为了解决这个问题,就规定了一套全球通用的协议标准,称互联网协议族(Internet Protocol Suite)。只要支持符合这一协议标准的协议,就能进入互联网(Internet)

互联网协议包含上百种协议,其中最重要的便是TCP/IP协议

IP协议负责把传输数据,它会把数据分成一块块,然后通过IP包,由发送端传输到接收端。由互联网线路复杂,两个计算机之间会有很多线路,所以IP协议并不能保证可以到达或者按顺序到达接收端。

TCP协议是建立在IP协议之上,它负责建立两个计算机之间的可靠连接,保证数据按量按顺序到达。如果出现包丢了,就会重新发送。

在错综复杂的互联网中,计算机用IP地址来标识自身的位置。

实际上IP地址是一个32位整数(IPv4),为了便于阅读,把32位整数按8位分组后以十进制表示,如:192.168.1.1

IPv6实际上是一个128位整数,把128位整数按16位分组后,以十六进制表示。
如:abab:910c:2222:5498:5475:1111:39f0:2520

一台计算机可以有多个IP地址,当一台计算机接入两个或多个网络时,如路由器,就会有两个或多个IP地址。

两个计算机之间通信仅靠IP地址是不行的,还需要多个端口来对应不同的网络程序。每个网络程序在进行通信时,会向操作系统申请唯一的端口号,当该进程需要多个连接时,就会申请多个端口。

一个完整的TCP报文需要包含源IP地址、目标IP地址、源端口、目标端口和需要传输的数据

TCP编程

在介绍TCP编程之前,我们要引入一个计算机术语——socket

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。——百度百科

socket在英文释义中是孔、插座的意思。在网络编程中,表示网络连接的一端。要实现网络连接,就要打开socket。而要打开socket,需要知道目标IP地址、端口号以及协议类型。

在Python中有socket库,可以用它来实现网络编程。

在建立TCP连接时,我们称主动发起连接的为客户端,被动响应连接的为服务器。

客户端

我们先进行客户端的编写。

  • 首先,引入socket库:
import socket
  • 然后,创建一个socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

这里的AF_INET表示指定使用IPv4协议,如果用IPv6,则为AF_INET6

SOCK_STREAM表示指定使用面向流的TCP协议。

  • 然后,建立连接:
s.connect(('ai.taobao.com', 80))

需要注意的是,这里的参数是一个tuple,里面包含目标IP地址和目标端口。

这里用域名ai.taobao.com可以自动转到淘宝服务器的IP地址。

80指的是目标端口。服务器所提供的每个服务会对应一个固定的端口号。而80端口就是Web服务的标准端口。还有其他如SMTP服务是25端口,FTP服务是21端口等。

小于1024的端口号是Internet标准服务的端口,大于1024的端口号可以任意使用

  • 然后,向服务器发送请求:

建立TCP连接后,就可以向百度服务器发送请求了。我们要求返回首页的内容:

s.send(b'GET / HTTP/1.1\r\nHost:ai.taobao.com\r\nConnection: close\r\n\r\n')

TCP连接创建的通道是双向的,即双方都可以给对方发送数据。

HTTP协议则规定发送信息的顺序——客户端必须先给服务端发送信息,服务端收到信息后才能给客户端发送信息。

发送的文本要符合HTTP格式。

  • 接下来,编写接收服务端信息部分:
buffer = []                  # 创建一个缓冲区,用于暂存接收数据
while True:
    d = s.recv(1024)         # 指定一次最多接收的数据字节数
    if d :
        buffer.append(d)     # 若有接收数据,则添加到缓冲区
    else: 
        break                # 直到数据接收完毕,退出循环

data = b''.join(buffer)      # 把数据存放到data变量中
 

recv(max)方法是指定一次接收的最大字节数,使用while循环接收数据,直到数据接收完毕就会退出循环。

  • 然后,关闭socket

调用close( )来关闭socket,这样就完成了网络通信客户端的编写:

s.close()
  • 最后,处理接收数据:

对接收的数据进行简单的处理,把HTTP头打印出来,把网页内容保存:

header, html = data.split(b'\r\n\r\n', 1)    # 分离HTTP头与网页内容
print(header.decode('utf_8'))                # 打印HTTP头
with open('baidu.html', 'wb') as f:          # 保存网页内容
    f.write(html)

这样,我们打开指定路径下的taobao.html就可以看到网页的内容。

我们来看一下完整的代码以及运行结果吧。

客户端:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.connect(('ai.taobao.com', 80))

s.send(b'GET / HTTP/1.1\r\nHost:ai.taobao.com\r\nConnection: close\r\n\r\n')

buffer = []                  # 创建一个缓冲区,用于暂存接收数据
while True:
    d = s.recv(1024)         # 指定一次最多接收的数据字节数
    if d :
        buffer.append(d)     # 若有接收数据,则添加到缓冲区
    else: 
        break                # 直到数据接收完毕,退出循环

data = b''.join(buffer)      # 把数据存放到data变量中
 
header, html = data.split(b'\r\n\r\n', 1)
print(header.decode('utf_8'))
with open('taobao.html', 'wb') as f:
    f.write(html)

运行结果:

HTTP/1.1 200 OK 
Date: Mon, 10 Sep 2018 14:24:38 GMT 
Content-Type: text/html;charset=UTF-8 
Transfer-Encoding: chunked 
Connection: close 
Vary: Accept-Encoding 
Vary: Accept-Encoding 
S: STATUS_NOT_EXISTED 
Set-Cookie: t=e4ad8c4112aa5205748b3bf8780e216d; Domain=.taobao.com; Expires=Sun, 09-Dec-2018 14:24:38 GMT; Path=/ 
Set-Cookie: cookie2=1e5f1cc4e8adf4b072d111c70933bea9;Domain=.taobao.com;Path=/;HttpOnly 
Set-Cookie: v=0; Domain=.taobao.com; Path=/ 
Set-Cookie: _tb_token_=e78b393e779e9; Domain=.taobao.com; Path=/ 
P3P: CP='CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR' 
Content-Language: zh-CN 
Server: Tengine/Aserver 
Timing-Allow-Origin: * 

在指定的路径下会生成一个taobao.html文件。

服务器

服务器负责响应连接。

它通过绑定一个固定的端口,来监听客户端的连接。一旦接收到某个客户端的连接,就创建一个socket,然后进行通信。

由于一个服务器端口可能会接收到多个客户端的连接,所以每个连接都需要一个新的进程或线程来处理,否则一个服务端口一次只能服务一个客户端。

下面我们编写一个简单的服务器,它接收客户端的连接,然后在接收的字符串上添加“hello”,并返回信息。

  • 首先,创建一个基于IPv4和TCP协议的socket
import socket

s = socket.socket(socket.AF_INET, slcket.SOCK_STREAM)

  • 然后,通过bind()方法绑定需要监听的端口:
s.bind(('127.0.0.1', 9999))

需要注意的是,这里的参数是一个tuple,含IP地址与端口号。

IP地址127.0.0.1是一个特殊地址,表示本机地址。若绑定这个地址,则客户端必须同时在本机运行才能连接。

由于我们编写的不是标准的服务端口,所以用9999端口号。

  • 然后,调用listen()方法监听绑定的端口,参数为最大的等待连接数:
s.listen(5)
print('Waiting for connecting...')

  • 接着,用while循环来永久接收客户端的连接:
while True:
    sock, addr = s.accept()                                  # 接收客户端的连接和地址
    t = threading.Thread(target=tcplink, args=(sock, addr)   # 创建新线程来处理TCP连接
    t.start()                                                # 启动线程

accept()方法会接收客户端的连接和地址。

通过threading.Thread()建立新线程来处理TCP连接。target为运行对象,args为传入参数。

  • 接着,我们来编写对响应连接部分:
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)                          # 休眠1s
        if not data or data.decode('utf-8') == 'exit':        # 如果没有接收到数据或收到'exit'则退出
            break
        sock.send(('Hello,%s!' % data.decode('utf-8')).encode("utf-8"))   # 返回客户端的信息
    sock.close()
    print('Connection form %s:%s closed' % addr)

这样我们就完成了服务器的编写,最后我们需要编写一个简单的客户端来测试该服务器的运行。

我们来看一下完整的代码以及运行结果吧。

服务器:

import socket,threading,time

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.bind(('127.0.0.1', 9999))

s.listen(5)
print('Waiting for connecting...')

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 form %s:%s closed' % addr)
    
while True:
    sock, addr = s.accept()                                   # 接收客户端的连接和地址
    t = threading.Thread(target=tcplink, args=(sock, addr))   # 创建新线程来处理TCP连接
    t.start()

客户端:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 9999))                           # 建立连接
print(s.recv(1024).decode('utf-8'))                      # 接收欢迎消息
for data in [b'Michael', b'Tracy', b'Sarah']:
    s.send(data)                                         # 发送数据
    print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()

运行结果:


简单TCP编程.png

UDP编程

TCP是通过建立可靠连接,以流的方式传输数据。

而UDP则是面向无连接的协议。只需要知道目标的IP地址和端口号就能传输数据,无需建立连接。

UDP的优点是速度快,缺点是不能保证数据能够到达。

相比TCP,由于不用建立连接,UDP编程简单很多,同样从服务器与客户端入手。

服务器:

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('127.0.0.1', 9999))
print('Bind UDP on 999...')
while True:
    data, addr = s.recvfrom(1024)
    print('Receive from %s:%s.' % addr)
    s.sendto(b'Hello,%s!' % data, addr)

注意这里SOCK_DGRAM指定socket类型是UDP。不用调用listen()方法,直接获取数据。

客户端:

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Ming', b'Hong', b'Leo']:
    s.sendto(data, ('127.0.0.1', 9999))
    print(s.recv(1024).decode('utf-8'))
s.close()

运行结果:


简单的UDP编程

可以发现,数据传输速度比TCP快很多。

一般要求速度快,但不要求可靠到达的数据,就可以用UDP协议。

还有一点是服务器绑定的TCP端口与UDP端口不冲突,也就是说TCP的9999端口与UDP的9999端口是可以各自绑定的。


以上就是本节的全部内容,感谢你的阅读。

下一节内容:电子邮件

有任何问题与想法,欢迎评论与吐槽。

和博主一起学习Python吧( ̄▽ ̄)~*

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容

  • 网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编...
    程序员欧阳阅读 2,014评论 1 37
  • 计算机网络概述 网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。 按照计算机网络的定义,通过一定...
    蛋炒饭_By阅读 1,224评论 0 10
  • 网络编程的概述 网络编程的实质就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。 一.OSI网络模型...
    思念挥霍阅读 375评论 0 0
  • 前言 最初我们迷茫,是不知道要去哪。知道去哪了,我们还迷茫,是因为不知该如何去。最终去了想去的地方,我们仍迷...
    爱读书的折耳猫阅读 606评论 0 6
  • 太子湾樱花
    涛叔Sqt阅读 113评论 0 3