python提供了两个不同层次的网络编程API:

  • 基于Socket的低层次网络编程,采用UDP, TCP等协议
  • 基于Url的高层次网络编程,采用 HTTP 和 HTTPS

网络编程的基础知识

网络结构:网络结构是网络的构建方式,目前流行的是客户端服务器结构网络和对等结构网络。

客户端服务器结构网络(C/S)是一种主从结构网络。服务器一般处于等待状态,如果有客户端请求,服务器响应请求,建立连接提供服务。服务器是被动的状态。例子:Web服务,邮件服务,文件传输服务。存在目的不同,但是结构类似。网络结构与设备种类无关,手机也可以当服务器。

对等结构网络(P2P)也叫点对点(Peer to Peer)结构网络。每个节点之间是对等的。每个节点既是服务器也是客户端。范围较小,通常在一间办公室和家庭中。非常适合移动设备间的网络通信。

TCP/IP协议:网络通信需要用到协议。TCP/IP协议是非常重要的,分为TCP协议和IP协议。
IP协议是一种低级的路由协议,它将数据分为很多小的数据包,然后通过网络发送到特定地点,但是无法保证所有包都能到达指定目的地,也无法保证包的到达顺序。
由于IP协议的不安全性,网络通信还需要TCP(传输控制协议)协议,是一种面向连接的可靠传输高级协议。保证未收到的包会进行重发,同时对数据包内容的准确性进行检查并保证数据包的顺序。所以TCP协议能够保证所有的数据包能够安全的按照顺序发送到特定地点。

IP地址:为了实现不同计算机之间的通信,每个计算机都必须有一个与众不同的标志,这就是IP地址。TCP/IP使用IP地址来标志源地址和目的地址。
最初的IP地址有32位,由四个8位的二进制组成。例如192.168.1.1,这种类型的地址通过IPV4指定。
而现在有种新的地址模式称为 IPv6, lPv6 使用 128 数字表示一个地址,分为八个
16 位块。尽管IPV6比较IPV4有很多优势,但是目前还是IPV4使用的较多。python语言同时支持IPV4和IPV6
IPv4 地址模式中IP地址分为A,B,C,D,E类。

  • A类地址用于大型网络,地址范围:1.0.0.1 — 126.155.255.254
  • B类地址用于中型网络,地址范围:128.0.0.1 — 191.255.255.254
  • C类地址用于小规模网络,地址范围:192.0.0.1 — 255.255.255.254
  • D类地址多用于目的地信息的传输和备用
  • E类地址保留,仅做实验和开发用
  • 一个特殊的IP地址,127.0.0.1,称为回送地址,即本机。

端口:一个IP地址代表一台计算机,一台计算机会运行很多个网络通信程序。这就需要不同的端口进行通信
TCP/IP系统中的端口是一个16位数字,范围是0-65535。小于1024的端口号是留给预定义服务的.

TCP Socket:Socket是指网络上的两个程序,通过一个双向的通信连接,实现数据的交换。

这个双向连接的一段,称为一个Socket。一个Socket由一个地址和一个端口唯一确定,一旦连接Socket还会包含本机和远程主机的IP地址和端口号。Socket是成对出现的。

python 判断 ip是否能ping通 python判断ipv4类型_服务器

TCP Socket通信过程

python 判断 ip是否能ping通 python判断ipv4类型_服务器_02


python提供了两个Socket模块,Socket和SocketServer,前者提供了标准的BSD Socket API. Socket的重点是网络服务器开发,提供了四个基本服务器类,简化服务器开发。

创建TCP Socket:

import socket
# socket.AF_INET: IP地址类型是IPV4。 IPV6的参数是 AF_INET6.  
# socket.SOCK_STREAM:设置通信类型是TCP
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Socket对象有很多方法,与TCP Socket服务器有关的方法如下:

socket.bind(address):绑定地址和接口。address是包含主机名(或IP地址)和端口的二元组对象。

socket.listen(backlog):监听端口。backlog是最大连接数,默认为1.

socket.accept():等待客户端连接,连接成功返回一个二元组对象(conn, address), conn是新的scocket对象,用来发送或接收数据,address是客户端的地址。

客户端socket方法:

socket.connect(address):连接服务器。address是一个包含主机地址(IP对象)和端口的二元组。

服务器和客户端Socket编程共用方法:

socket.revc(buffsize):接收TCP Socket数据,返回一个字节序列对象。参数buffsize指定一次接收的最大字节数,如果要接收的数据量大于buffsize,则需要多次调用该方法进行接收

socket.send(bytes):发送TCP Socket数据,将bytes数据发送到远程Socket.返回发送成功的字节数。如果发送的数据量过大,需要多次调用这个方法。

socket.sendall(bytes):发送TCP Socket数据,发送成功返回None,失败返回异常。与socket.send(bytes)不同的是,该方法会一直发送bytes数据,直至数据发送完毕或发生异常。

socket.settimeout(timeout):设置超时时间。timeout是一个浮点数,单位是s。设置为None则永不超时。一般超时时间都是在刚创建socket时创建。

socket.close():该方法可以释放资源。但是不一定会立刻关闭连接。如果想要立刻关闭连接,需要在之前执行shutdown()方法。

python中socket对象是可以进行垃圾回收的,当socket对象被回收时会自动关闭,但还是建议显式的调用close()进行关闭。

案例:简单聊天工具

import socket
import threading


class TestSocketServerThread(threading.Thread):
    def __init__(self):
        super(TestSocketServerThread, self).__init__()

    def run(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建socket
        # IP地址为空,系统会自动分配本机可用的IP地址
        s.bind(("", 8888))  # 绑定地址
        s.listen()  # 监听本机8888号端口
        print("服务器启动...")
        # 等待客户端连接
        conn, address = s.accept()
        # 客户端连接成功
        print(address)
        # 接收数据 返回的是字节序列对象。
        # decode()可将字符序列转换为字符串对象,可指定编码,默认为utf-8
        data = conn.recv(1024)
        print("从客户端接收消息:{0}".format(data.decode()))
        # 给客户端发送消息
        conn.send("你好".encode())
        # 释放资源
        conn.close()
        s.close()


class TestSocketClientThread(threading.Thread):
    def __init__(self):
        super(TestSocketClientThread, self).__init__()

    def run(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建socket
        # '127.0.0.1'是远程服务器IP地址或主机名
        s.connect(('127.0.0.1', 8888))  # 绑定地址
        # 给客户端发送消息
        # 在字符串前加 b,可将字符串转换为字节序列,这种方法只适合ASCII字符串
        s.send(b'hello')
        # 从服务器端接收数据
        data = s.recv(1024)
        print("从服务端接收消息:{0}".format(data.decode()))
        # 释放资源
        s.close()


ServerThread = TestSocketServerThread()
ClientThread = TestSocketClientThread()
ServerThread.start()
ClientThread.start()

简单的文件上传工具案例

import socket
import threading

class TestScoketServerthread(threading.Thread):
    def __init__(self):
        super(TestScoketServerthread, self).__init__()

    def run(self):
        Host = ""
        Post = 8888
        f_name = "aaaa.txt"
        # 使用with as自动管理Socket对象
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.bind((Host, Post))
            s.listen(10)
            print("服务器已启动...")
            while True:
                with s.accept()[0] as conn:
                    # 创建字节序列对象列表,作为接收数据的缓冲区
                    buffer = []
                    while True:
                        data = conn.recv(1024)
                        if data:
                            buffer.append(data)
                        else:
                            break
                b = bytes().join(buffer)
                with open(f_name, 'wb') as f:
                    f.write(b)
                print("服务器接收完成")


class TestScoketClientthread(threading.Thread):
    def __init__(self):
        super(TestScoketClientthread, self).__init__()

    def run(self):
        HOST = '127.0.0.1'
        PORT = 8888
        f_name = "bbbb.txt"

        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect((HOST, PORT))
            with open(f_name, 'rb') as f:
                data = f.read()
                s.sendall(data)
                print("客户端数据上传成功")


server = TestScoketServerthread()
client = TestScoketClientthread()
server.start()
client.start()

UDP(用户数据报协议):
无连接,对系统资源要求较少。
不能可靠的寄到目的地,传达时的顺序不能保证。
但是对网络游戏,在线视频等传输快,实时性高,质量可稍差一点的传输。

Socket模块中的UDP API与TCP类似,都是使用socket对象,只是参数有所不同。

import socket
# socket.AF_INET: IP地址类型是IPV4。 IPV6的参数是 AF_INET6.  
# socket.SOCK_DGRAM:设置通信类型是UDP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

UDP Server Socket方法

socket.bind():不需要accpet和listen。upd传输不需要建立连接

服务器与客户端socket共用方法

socket.recvfrom(buffsize):接收UDP Socket数据。
返回二元组(data, address),前者是接收的字符序列对象,后者是发送数据的Socket地址。
参数buffsize指定一次接收的最大数据量,如果接收数据量大于buffsieze,则需要多次调用该方法发送数据

socket.sendto(bytes, address):将字符序列bytes,发送到地址为address的远程socket。(address是一个socket对象)
若发送数据量较大,则需要多次运行此方法

socket,settimeout(time):设置超时时间,同TCP

socket.close():关闭socket,同TCP

案例:UPD的简单聊天工具
UDP传输工具与TCP有所不同,UDP接收数据时,并不能了解数据是否已经传输完成,因此在上传数据时,通常会在上传数据完成后上传一个关键字,服务器接收到关键字后,能够判断数据已经接收完毕。

import socket
import threading


class TestUDPServerThread(threading.Thread):
    def __init__(self):
        super(TestUDPServerThread, self).__init__()

    def run(self):
        # 建立socket, 参数2使用UDP传输
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.bind(('', 8888))
        print("服务器启动")
        data, client_address = s.recvfrom(1024)
        print("从客户端接收的数据: {0}".format(data.decode()))
        # 向客户端发送数据
        s.sendto("你好".encode(), client_address)
        s.close()


class TestUDPClientThread(threading.Thread):
    def __init__(self):
        super(TestUDPClientThread, self).__init__()

    def run(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        server_address = ('127.0.0.1', 8888)
        s.sendto(b'hello', server_address)
        data, _ = s.recvfrom(1024)
        print("从服务器接收的数据: {0}".format(data.decode()))
        s.close()


server = TestUDPServerThread()
client = TestUDPClientThread()
server.start()
client.start()

UDP文件上传工具示例

import socket
import threading


class TestUDPServerThread(threading.Thread):
    def __init__(self):
        super(TestUDPServerThread, self).__init__()

    def run(self):
        # 建立socket, 参数2使用UDP传输
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.bind(('', 8888))
        print("服务器启动")
        data, client_address = s.recvfrom(1024)
        print("从客户端接收的数据: {0}".format(data.decode()))
        # 向客户端发送数据
        s.sendto("你好".encode(), client_address)
        s.close()


class TestUDPClientThread(threading.Thread):
    def __init__(self):
        super(TestUDPClientThread, self).__init__()

    def run(self):
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        server_address = ('127.0.0.1', 8888)
        s.sendto(b'hello', server_address)
        data, _ = s.recvfrom(1024)
        print("从服务器接收的数据: {0}".format(data.decode()))
        s.close()


server = TestUDPServerThread()
client = TestUDPClientThread()
server.start()
client.start()