Advance Python 09 :python socket编程

一、弄懂HTTP、Socket、TCP这几个概念

网络由下往上分为物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层。socket则是对TCP/IP协议的封装和应用。也可以说,TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。

1.1、概念

1、HTTP:

http协议

  • **官方概念:**HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。

  • 白话概念:HTTP协议就是 服务器(Server)和客户端(Client)之间进行数据交互(相互传输数据)的一种形式。

==》 遵从http协议,则服务器和客户端可以进行简单的数据交互

HTTP工作原理

HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。

2、Socket:

socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。 实际上socket是对 TCP/IP 协议的封装,它的出现只是使得程序员更方便地使 TCP/IP 协议栈而已。socket本身并不是协议,它是应用层与 TCP/IP 协议族通信的中间软件抽象层,是一组调用接口(TCP/IP网络的API函数)

​ Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

Python 网络编程 | 菜鸟教程 (runoob.com)

3、TCP:

传输控制协议

4、OSI七层:

引子:

​ 一个完整的计算机系统是由硬件、操作系统、应用软件三者组成,具备了这三个条件,一台计算机系统就可以自己跟自己玩了(打个单机游戏,玩个扫雷啥的)

如果你要跟别人一起玩,那你就需要上网了,什么是互联网?

​ 一系列统一的标准,这些标准称之为互联网协议,互联网的本质就是一系列的协议,总称为‘互联网协议’(Internet Protocol Suite).

​ 互联网协议的功能:定义计算机如何接入internet,以及接入internet的计算机通信的标准。
在这里插入图片描述

详见网络通信原理:http://www.cnblogs.com/linhaifeng/articles/5937962.html

5、关于TCP/IP和HTTP协议的关系:

我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容,如果想要使传输的数据有意义,则必须使用到应用层协议,应用层协议有很多,比如HTTP、FTP、TELNET等,也可以自己定义应用层协议。WEB使用HTTP协议作应用层协议,以封装HTTP文本信息,然后使用TCP/IP做传输层协议将它发到网络上。

而socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。实际上,Socket跟TCP/IP协议没有必然的联系。Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以说,Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、listen、connect、accept、send、read和write等等。

6、socket和TCP/IP协议关系:

TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。这个就像操作系统会提供标准的编程接口,比如win32编程接口一样,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。

7、HTTP和socket关系:

HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。传输层的TCP是基于网络层的IP协议的,而应用层的HTTP协议又是基于传输层的TCP协议的,而Socket本身不算是协议,就像上面所说,它只是提供了一个针对TCP或者UDP编程的接口。

1.2、将概念串起来

整个计算机网络都是由协议组成的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7a2Ew58D-1644504122428)(C:\Users\pc\AppData\Roaming\Typora\typora-user-images\image-20220210200844228.png)]

A是client端,B是服务器端,当A向B发送请求时,数据传输的时候(平时使用requests、urllib请求数据),在我们操作系统和网络之中要经历5层网络模型。

A传送数据是从上到下的(拆包),B接受数据是从下到上的(组包)。

浏览器也是实现了http协议,http协议是构建与TCP、IP…协议之上。可以看到应用层有很多协议,HTTP、SMTP、DNS…。

如果我们要实现TCP、UDP协议,自己写协议比较复杂,一般都是操作系统提供的。操作系统提供了一个Socket,可以理解为一个API,不属于任何一个协议,我们通过Socket可以和传输层打交道。

把Socket理解为一个插座,数据理解为电力,如果我们要充电,就需要插座,而且电在传输过程中要经过很多变压器(传送层下面的层),将设备和插座连接起来,就可以使用电了。

在TCP协议上提供了一个接口(Socket),通过Socket和底层协议建立了一个连接。

HTTP协议是单向的,A发送请求,B响应,但B不能主动发送消息给A。

但是TCP协议是双向的,只要不主动断开,就一直在存在联系。

Socket可以操控TCP,这样就可以实现自己的协议,不需要去TCP协议上进行开发。这个在很多情况下是很实用的,比如聊天工具,会在应用层实现一套自己的协议,通过Socket和TCP打交道。

二、通过Socket实现client和Server通信

2.1、概念

Socket编程一般会涉及Socket_client和Socket_server编程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DmshhxbI-1644504122428)(C:\Users\pc\AppData\Roaming\Typora\typora-user-images\image-20220210201443683.png)]

左侧是server端,右侧是client端。

server端,比如写一个web系统,部署到服务器。

client端,比如常见的PC软件、浏览器。

爬虫是一个很典型的client端,请求server端的数据。

自己实现一个web server端,有个特点是必须随时处于监听和服务的状态,因为不知道客户端什么时候发送请求过来。

绑定:

协议(Socket本身是接口,用来连接应用层和传输层)、

地址(Socket可以监听本地ip,也可以监听对外Ip)

端口(每一个应用程序只能占用一个端口,使用Socket指定端口,就可以把应用程序和端口绑定起来,这样在多个应用程序下数据传输不会混乱,特定的应用程序使用某一个端口进行数据传输),相同的应用程序可以使用一个端口(多线程中运行了相同的程序)。

listen:

监听Socker连接请求

绑定完之后就可以开始监听,监听之后调用accept函数会阻塞连接请求,直到client端发起连接请求(TCP,三次握手),成功后就创建一个Socket连接。然后运行recv函数获取数据(需要client端send发送数据过来),之后可以打印并做各种操作(服务端可以send数据给client端,理论上只要有Socket连接,Server端就可以一直向client端发送数据,但是在HTTP请求中,一般只进行一次server和client之间的数据传输,之后连接就会关闭)。

2.2、示例

可以看一个服务端的简单例子

server:

import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # AF_INET是ipv4,socket.SOCK_STREAM是一个协议
server.bind(('0.0.0.0', 8000))# IP地址,端口
server.listen()
sock, addr = server.accept() # accept 会返回一个sock和addr
#获取从客户端发送的数据
data = sock.recv(1024) #1024=1k的数据
print(data.decode("utf8")) # data是byte类型

server.close()
sock.close()
bobby

client:

import socket

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1', 8000)) # 要明确指明ip地址
client.send("bobby".encode("utf8"))

client.close()

这样通信就完成了,再修改一下,让服务端发送数据给client端

server:

import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # AF_INET是ipv4,socket.SOCK_STREAM是一个协议
server.bind(('0.0.0.0', 8000))# IP地址,端口
server.listen()
sock, addr = server.accept() # accept 会返回一个sock和addr
#获取从客户端发送的数据
data = sock.recv(1024) #1024=1k的数据
print(data.decode("utf8")) # data是byte类型
sock.send("hello {}".format(data.decode("utf8")).encode("utf8")) # 从client传过来的数据是byte类型,先decode为str,再encode为byte,传入client端

server.close()
sock.close()
bobby

client:

import socket

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(('127.0.0.1', 8000)) # 要明确指明ip地址
client.send("bobby".encode("utf8"))
data = client.recv(1024)
print (data.decode("utf8"))

client.close()
hello bobby

三、Socker实现聊天和多用户连接

server:

import socket

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # AF_INET是ipv4,socket.SOCK_STREAM是一个协议
server.bind(('0.0.0.0', 8000))# IP地址,端口
server.listen()
sock, addr = server.accept() # accept 会返回一个sock和addr
# server是用来监听的,sock才是用户连接的socket
#获取从客户端发送的数据
#一次获取1k的数据
while True:
    data = sock.recv(1024) # 假设1024字节就可以把数据取完
    print(data.decode("utf8"))
    re_data = input()
    sock.send(re_data.encode("utf8"))

client:

import socket

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 对用户来说,只需要一个client就可以了
client.connect(('127.0.0.1', 8000)) # 要明确指明ip地址
while True:
    re_data = input()
    client.send(re_data.encode("utf8"))
    data = client.recv(1024)
    print (data.decode("utf8"))

但是问题是server端只能接受一个请求,一个用户进来后,就一直在while循环里出不来。

如何实现多用户连接呢,可以用线程,每一个socket是一个线程,主线程接受请求。

server:

import socket
import threading

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # AF_INET是ipv4,socket.SOCK_STREAM是一个协议
server.bind(('0.0.0.0', 8000))# IP地址,端口
server.listen()

# server是用来监听的,sock才是用户连接的socket
#获取从客户端发送的数据
#一次获取1k的数据
def handle_sock(sock, addr):
    while True:
        data = sock.recv(1024) # 假设1024字节就可以把数据取完
        print(data.decode("utf8"))
        re_data = input()
        sock.send(re_data.encode("utf8"))

while True:
    sock, addr = server.accept()
    # 用线程去处理新接收的连接(用户)
    client_thread = threading.Thread(target=handle_sock, args=(sock, addr)) # target必须为函数名
    client_thread.start()

client:

import socket

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 对用户来说,只需要一个client就可以了
client.connect(('127.0.0.1', 8000)) # 要明确指明ip地址
while True:
    re_data = input()
    client.send(re_data.encode("utf8"))
    data = client.recv(1024)
    print (data.decode("utf8"))

四、socket模拟http请求

requests ->urllib -> socket

requests 基于urllib ,urllib 基于socket,凡是网络相关的连接(比如数据库连接,进程间的通信等)最底层都是用socket来连接的。

# requests -> urlib -> socket
import socket
from urllib.parse import urlparse  # urlparse是用来解析url的,不是用来发起请求的


def get_url(url):
    # 通过socket请求html
    url = urlparse(url)  # 需要先进行url解析
    host = url.netloc  # url主域名
    path = url.path  # url子路径
    if path == "":  # 如果直接请求主域名
        path = "/"

    # 建立socket连接
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect((host, 80))  # HTTP请求都是80端口
    # http协议是基于TCP协议的
    # GET {} 第一行必须这样写
    client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(path, host).encode("utf8"))
    data = b""  # 从server获取的数据为byte
    while True:
        d = client.recv(1024)  # 临时获取的数据
        if d:
            data += d
        else:  # 如果没有数据,则退出
            break

    data = data.decode("utf8")
    html_data = data.split("\r\n\r\n")[1]  # 去掉header信息
    print(html_data)
    client.close()


if __name__ == "__main__":
    get_url("http://www.baidu.com")

<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">......
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DLNovice

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值