python-socket编程

python-socket编程

五层网络模型

应用层

功能:文件传输,电子邮件,文件服务,数据传输
TCP/IP协议:HTTP,FTP,SMTP,DNS,TELNET,SSH

传输层

功能:提供端口对端口的接口
TCP/IP协议:TCP-UDP

网络层

功能:为数据包选择路由
TCP/IP协议:IP,ICMP

数据链路层

功能:传输有地址的帧,错误检测功能
TCP/IP协议:ARP

物理层

功能:物理媒体,光纤
TCP/IP协议:1000BASE-SX

每次的网络传输请求都是通过 应用层–->传输层–->网络层–->数据链路层–->物理层传递。
除了应用层,其他的层都由操作系统,物理设备完成,所以通过操作系统提供的接口socket直接走传输层(通过TCP链接,socket不属于网络协议,属于自己定义的协议)可以完成大部分不同协议的网络请求。

c/s实现通信

使用socket实现客户端与服务端的通信,偷一张图(socket的固定编程模式)
TIM%E6%88%AA%E5%9B%BE20190225114621.png

服务端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 指定网络类型,指定类型对应的协议(TCP协议)
# AF_INET = 2
# # IPV4
# AF_INET6 = 23
# # IPV6
# AF_IPX = 6
# # UNIX下的进程间通信

s.bind(('0.0.0.0',9999))
# 绑定IP与端口,传入元组类型,
s.listen()
# 开始监听绑定的IP与端口

sock,addr = s.accept()
# sock是传输对象,addr是传输对象的IP地址

data = sock.recv(1024)
# 获取客户端发来的1kb大小的始据
print(data.decode('utf-8'))
# 打印出客户端发来的数据,返回的是bytes类型,需要编码成utf-8
sock.send('总部收到总部收到'.encode('utf-8'))
# 然后向客户端发送一条数据
s.close()
sock.close()
# 关闭连接

客户端代码:

1
2
3
4
5
6
7
8
9
10
import socket
c = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
c.connect(('127.0.0.1',9999))
c.send('shuke呼叫总部'.encode('utf-8'))
# 因为传递的是bytes类型的数据,需要编码
data = c.recv(1024)
# 客户端接收服务端发来的1kb数据
print(data.decode('utf-8'))
# 打印服务端返回的数据
c.close()

socket的编码模式基本上可以说是固定的,套用代码即可。
先运行服务端代码,再运行客户端代码:
客户端返回结果:

1
shuke呼叫总部

服务端返回结果:

1
总部收到总部收到

这个就是固定的套路代码,如果想要让服务端一直接受客户端的请求的话,在这里修改代码即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
s.bind(('0.0.0.0',9999))
# 绑定IP与端口,传入元组类型,
s.listen()
# 开始监听绑定的IP与端口
while 1:
sock,addr = s.accept()
# sock是传输对象,addr是传输对象的IP地址

data = sock.recv(1024)
# 获取客户端发来的1kb大小的始据
print(data.decode('utf-8'))
# 打印出客户端发来的数据,返回的是bytes类型,需要编码成utf-8
sock.send('总部收到总部收到'.encode('utf-8'))
# 然后向客户端发送一条数据
sock.close()
# 关闭连接

注意socket发送的内容必须要是bytes类型的(python3),这里说明一下:

1
2
3
4
5
6
7
8
对于bytes编码的数据来说
只有decode方法
就是把bytes编码的数据转换成utf-8编码

对于utf-8编码的数据来说
只有encode方法
就是把utf-8编码的数据转换成bytes编码,如果要转成其他编码
.encdoe('gbk')即可

C/S端相互发消息

  1. 服务端发送代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import socket
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    s.bind(('0.0.0.0',9999))
    s.listen()
    sock, addr = s.accept()

    while 1:
    inp = input('输入消息:')
    sock.send(inp.encode('utf-8'))
    data = sock.recv(1024)
    print('接收消息:'+data.decode('utf-8'))
  2. 客户端发送代码
    1
    2
    3
    4
    5
    6
    7
    8
    import socket
    c = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    c.connect(('127.0.0.1',9999))
    while 1:
    data = c.recv(1024)
    print('接收消息:'+data.decode('utf-8'))
    inp = input('输入消息:')
    c.send(inp.encode('utf-8'))
    服务端返回结果:
    1
    2
    3
    输入消息:我是你爸爸
    接收消息:我是你爷爷
    输入消息:
    客户端返回结果:
    1
    2
    接收消息:我是你爸爸
    输入消息:我是你爷爷

    多用户连接

    从上面的代码中一个sock只能服务一个客户端,服务端只能服务一个对象,使用多线程即可完成一个服务端服务大量的客户端。
    服务端代码:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import socket
    import threading
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    s.bind(('0.0.0.0',9999))
    s.listen()

    def handle_sock(sock,addr):
    while 1:
    inp = input('输入消息:')
    sock.send(inp.encode('utf-8'))
    data = sock.recv(1024)
    print('接收消息:' + data.decode('utf-8'))

    while 1:
    sock, addr = s.accept()
    clinent_thread = threading.Thread(target=handle_sock,args=(sock,addr))
    clinent_thread.start()
    客户端代码:
    1
    2
    3
    4
    5
    6
    7
    8
    import socket
    c = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    c.connect(('127.0.0.1',9999))
    while 1:
    data = c.recv(1024)
    print('接收消息:'+data.decode('utf-8'))
    inp = input('输入消息:')
    c.send(inp.encode('utf-8'))
    这个时候运行两个客户端发送请求一样可以被服务端获取到。

模拟HTTP请求

requests本质上调用urllib发起网络请求,urllib通过调用socket发起网络请求。模拟socket对HTTP请求就是对本质上进行重写编程。
具体实现看代码,其实很简单的,不多描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import socket
from urllib.parse import urlparse
# 对url做解析

def get_url(url):
url = urlparse(url)
# 对传入的网址进行解析,比如传入http://www.langzi.fun/admin.php
host,path = url.netloc,url.path
# 获取传入网址的主域名和后面的url路径
if path == '':
path = '/'
c = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
c.connect((host,80))
headers = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
# 绑定ip与端口
c.send('GET {}\r\nHost:{}\r\nUser-Agent:{}\r\n'.format(path,host,headers).encode('utf-8'))
# 发送请求,请求方式为GET 内容是url的路径,然后分隔换行
# 发送请求,请求的HOST主机就是主域名
# 在这里可以把请求头,cookie加进来

data = b''
# 获取数据
while 1:
d = c.recv(1024)
if d:
data +=d
else:
break
print(data.decode('utf-8'))

get_url('http://www.lancygroup.com/')

refs

原文-socket编程

本文标题:python-socket编程

文章作者:shuke

发布时间:2020年04月23日 - 16:04

最后更新:2020年04月23日 - 16:04

原始链接:https://shuke163.github.io/2020/04/23/python-socket%E7%BC%96%E7%A8%8B/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------

本文标题:python-socket编程

文章作者:shuke

发布时间:2020年04月23日 - 16:04

最后更新:2020年04月23日 - 16:04

原始链接:https://shuke163.github.io/2020/04/23/python-socket%E7%BC%96%E7%A8%8B/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%