python网络编程细节讲解socket 到 小型udp聊天器及 tcpweb服务器, 小型下载器(进度更新及大文件传输)简单实现。

一)什么是Socket?

  • 可能介绍时稍加自己理解,有偏差的地方,还请见谅。

(1)首先我们了解计算机网络中的信息传输过程

  • (有七层,五层,四层计算机网络原理体系结构)我说下我学习的五层结构。
  • (1)主机A发送数据: 应用层-->运输层-->网络层-->数据链路层-->物理层
  • (2)中间经过路由器物理层-->数据链路层-->网络层
  • (3) 知道到达主机B物理层-->数据链路层-->网络层-->运输层-->应用层

(2)为何使用socket

  • 我们不再去细说每层所具有的功能,和使用的协议。但当经过运输层时,所有的外层首部字段(比如ip等)都将褪去(也就是剩下tcp/udp层次的)。 ip协议只是负责将数据传送给指定的主机。 到达运输层后,选用 tcp/udp 协议,利用端口,实现端到端的通信,也就是应用进程之间的通信。
  • 我的理解是,运输层通过tcp/udp 协议将数据传送给了应用进程,也就是应用层,应用层根据自己的协议来规实现功能。
  • (重点):那么如何利用运输层传递的信息数据呢?那就是socket!

(3) socket介绍(书上原话):

  • 大部分的网络协议都是由软件实现的(特别是协议栈中的高级协议),绝大多数的计算机系统都将运输层以下的网络协议在操作系统内核实现。(重点)应用程序想要执行网络操作,必须通过操作系统为应用程序操作网络所提供的的接口,这个接口通常称为网络应用编程接口 (Application Programming interface)。
  • 大多数操作系统都具有自己的网络API,但是随着时间的推移,套接字(Socket)API得到广泛应用。
  • 套接字API 定义了一组数据结构和操作。其中最关键的就是套接字数据结构(简称套接字)。套接字是一个非常复杂的数据结构,包括进行网络操作所需的各种资源(如缓存),各种参数(地址,端口号,协议类型等)。应用进程想要进行网络操作,必须先调用套接字API中定义的操作创建套接套接字数据结构,已获得进行网络操作所需的资源。
  • 套接字数据结构位于操作系统内核,应用程序不能直接访问数据结构,需要通过创建操作返回的套接字描述符来操作数据结构,此后,应用程序所进行的网络操作(建立连接,收发数据,调整网络通信参数等)都必须以该描述符为参数调用套接字API中的操作完成。

相信你已经初步了解了套接字。我在读到这里时,也豁然开朗了很多。

二)Tcp套接字,客户端/服务端调用套接字API基本流程

在这里插入图片描述

三)python socket 标准库基本使用介绍(官方文档)

(1)socket库介绍

  • 可以看到这个库,也是对底层调用 套接字API的封装,使操作更简洁,更智能。
  • socket-底层网络接口
    在这里插入图片描述

(2)用法:

1.创建套接字对象:  字段均可在官方文档上查阅:
import socket
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
"""
:param family:协议,一般表示ivp4(默认) 或者 ivp6
:param type: 套接字类型, SOCK_STREAM(默认)tcp 流

使用给定的地址族,套接字类型和协议号创建一个新的套接字。 
地址族应为AF_INET(默认设置),AF_INET6,AF_UNIX,AF_CAN,AF_PACKET或AF_RDS。 
套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。
协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。 
如果指定了fileno,则其他参数将被忽略,从而导致具有指定文件描述符的套接字返回。 
与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的套接字。 
这可能有助于使用socket.close()关闭分离的套接字。
"""

(3)什么是UDP报文首部格式:

** UDP

  • 用户数据报协议 UDP只在IP的数据报服务上增加了端口功能,特点:UDP是无连接的,尽最大努力交付的,没有拥塞控制,面向报文的,支持一对一,一对多,多对一和多对多交互通道。
    在这里插入图片描述

(4) udp的小例子(发送数据)

  • udp实现比较简单,直接创建连接,然后便可以发送数据,单工通信。
  • 重点是上面的tcp基本流程
  1 import socket
  2 
  3 def main():
  4     """ udp小例子"""
  5     # 创建套接字对象 (使用socket.SOCK_DGRAM,也就是udp数据报)
  6     udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  		# 也可进行端口绑定 bind,否则随机分配端口
  7     # 选择发送方地址(元组形式的ip地址和端口号)
  8     dest_addr = ('10.212.xxx.xxx', 8080)
  9     # 发送信息 
 10     send_data = input("请输入要发送的数据:")
 11     udp_socket.sendto(send_data.encode("utf-8"), dest_addr)
 12     # 关闭套接字
 13     udp_socket.close()
 14     print("-----end----")
 15 
 16 
 17 if __name__ == "__main__":
 18     main()

在这里插入图片描述

  • 本例子是在VMware虚拟机 uubntu系统上面编写,往物理机 windows 上发送数据,利用模拟软件接收。
  • 运行
    在这里插入图片描述
  • 结果:
    在这里插入图片描述

(5)UDP的小例子(接收数据)

  1 import socket
  2 
  3 def recv():
  4 
  5     # 1.创建套接字
  6     udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  7     # 2. 绑定信息
  8     local_addr = ("", 8081)   # ip地址和端口号,一般ip地址不用写,表示主机任
    何一个IP地址
  9     udp_socket.bind(local_addr)
 10 
 11     # 3. 等待对方发送数据
 12     print("正在等待接收数据--:")
 13     recv_data = udp_socket.recvfrom(1024) # 最大接收字节数
 14 
 15     # 4. 显示接受的数据
 16     print("接收到的数据为:")
 17     print(recv_data)
 18 
 19     # 5. 关闭套接字
 20     udp_socket.close()
 21     print('----end------')
 22 
 23 if __name__ == "__main__":
 24     recv()

  • 绑定信息
    bind() 函数
  • 接收函数
    在这里插入图片描述

(6) udp 聊天器的小例子

  1 import socket
  2 
  3 class Chat():
  4     """ udp简单聊天器 """
  5     def __init__(self):
  6         """ 初始化 """
  7         self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  8         self.udp_socket.bind(("", 7999))
  9 
 10     def welcome(self):
 11         print("欢迎登陆!输入序号开始聊天")
 12         print("1. 选择聊天对象")
 13         order = input()
 14         if order == "1":
 15             self.people()
 16 
 17     def people(self):
 18         print("以下是聊天列表:请选择序号进行聊天~")
 19         print("1.小明")
 20         order = input()
 21         if order == "1":
 22             print("开始聊天:输入 exit结束聊天")
 23 
 24     def send(self, send_data):
 25         self.udp_socket.sendto(send_data.encode("gbk"), ("192.168.91.128", 8080))
 26         print("内容已发送")
 27 
 28     def recv(self):
 29         recv_data = self.udp_socket.recvfrom(1024)
 30 #       print("%s,小明:%s" % (str(recv_data[1]),recv_data[0].decode("gbk") ))
 31         print(recv_data[0].decode("utf-8"))
 32         print("完成over~~")
 33 
 34     def chat(self):
 35         while True:
 36             send_data = input("请输入想要说的内容:")
 37             if send_data == "exit":
 38                 print("聊天结束!")
 					self.udp_socket.close()
 39                 break
 40             self.send(send_data)
 41             print("正在等待对方回应:...")
 42             self.recv()
 43 
 44 if __name__ == "__main__":
 45     a = Chat()
 46     a.welcome()
 47     a.chat()

  • 效果(比较死板,只能收发收发的运行)
    在这里插入图片描述

(四)TCP介绍及 客户端与服务器简单实现

(1)什么是tcp:

  • 传输控制协议(TCP)提供全双工,可靠交付服务。
  • TCP是面相连接的(字节流),并且在运输层时使用了流量控制和拥塞控制机制。
  • 与upd不同,tcp的端口队列不同,tcp的发送缓存和接收缓存都是分配给一个连接的,而不是一个端口。TCP的链接由四元组(源IP地址,源端口号 ,目的IP地址,目的端口号)标识。

(2) TCP是区分客户端和 服务器的

  • (二) 中介绍的客户端服务器调用套接字接本流程。
    1. 创建套接字
    1. 对于客户端程序, 要做的就是 connect 链接服务器。服务器端,需要绑定,监听,和接收,返回新的发送套接字
  • 课本C语言展示:
    在这里插入图片描述
    在这里插入图片描述

(3)服务器示例

  • 监听
    在这里插入图片描述

  • 接收链接
    在这里插入图片描述

  • 接收请求数据
    在这里插入图片描述

  • 接收套接字,发送数据
    在这里插入图片描述

  • 客户端程序发送请求; 一般都是(IP地址, 端口号)
    在这里插入图片描述

  • 代码展示:

  • 功能: 同一时刻为一台客户端服务,服务完成后,允许其他客户端链接。

  1 import socket
  2 
  3 def server():
  4     # 1. 创建套接字
  5     tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  6     # 2. 绑定本地信息(ip,端口)
  7     tcp_socket.bind(("", 7890))
  8     # 3.设置为监听状态(监听套接字)
  		# 连接数,这个数字还是有操作系统决定的,根据操作系统不一样,能连接多少也不一样
  9     tcp_socket.listen(128)
 10     print("我是服务器,我现在正在堵塞中,等待监听客户端请求。。。。")
 11 
 12     while True:
 13         # 4.等待客户端的链接(产生指定的接收套接字,服务该链接),采用阻塞的方式!
 14         client_socket, client_addr = tcp_socket.accept()
 15         print("我同意客户端的链接请求,下面是接受套接字为您服务。。。。")
 16         while True:
 17             # 5.接受对方(客户端)发送的数据请求(接收套接字),接受到的只是数据,不包含地址信>    息
 18             recv_data = client_socket.recv(1024).decode("utf-8")
 19             print("接收到的数据为: ", recv_data)
 20 
 21             #  如果发送的是 exit, 或者主动关闭客户端链接,那么服务结束(当对方客户端主动close>    关闭自己链接时,服务器会受到空值)
 22             if recv_data == "exit" or recv_data == '':
 23                 print("服务结束")
 24                 break
 25 
 26             # 回复给客户端的请求
 27             client_socket.send("hello world".encode("utf-8"))
 28 
 29             # 6.关闭套接字
 30         print("关闭客户端")
 31         client_socket.close()
 32     tcp_socket.close()
 33 
 34 
 35 if __name__ == "__main__":
 36     server()

  • 运行示例结果
    在这里插入图片描述

(五)实现简单的下载器

  • 简单的模拟文件的下载请求,和服务器传输下载文件。
  • 客户端
  1 import socket
  2 import sys
  3 import time
  4 
  5 
  6 _output = sys.stdout
  7 
  8 def client():
  9     """ 请求下载文件的客户端 """
 10     # 1. 创建套接字
 11     dw_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 12     # 2. 连接服务器
 13     dest_ip = input("请输入下载服务器的ip地址:")
 14     dest_port =int(input("请输入端口:"))
 15     dw_client.connect((dest_ip, dest_port))
 16 
 17     # 3. 获取下载文件名
 18     dw_filename = input("请输入要下载的文件名字:")
 19     # 4. 发送文件名字到服务器
 20     dw_client.send(dw_filename.encode("utf-8"))
 21 
 22 
 23     # 7.保存文件中的数据到文件中
 24     i = 1
 25     with open("[new]" + dw_filename, "ab+") as f:
 26         while True:
 27             #  接受文件,大小为1M  1024 --> 1k
 28             dw_data = dw_client.recv(1024*1024)
 29             if dw_data == b'001':
 30                 print('接收完毕!')
 31                 break
 32             f.write(dw_data)
 33             time.sleep(0.1)
 34             _output.write("\r传输数据次数: %d" % i)
 35             i += 1
 36     _output.flush()
 37     print("文件下载完毕请查看!,本次共传输:  %d 次!" % i)
 38     # 8. 关闭套接字
 39     dw_client.close()
 40 
 41 
 42 if __name__ == "__main__":
 43     client()
        

  • 下载服务器端:
  1 import socket
  2 import time
  3 
  4 def read_data(f, n):
  5     """ 生成器形式,
  6         当文件过大时,我们需要分次读取,读取太大要考虑内存大小,读取太小要考虑到读取操作上的>    开销
  7     """
  8     content = b''
  9     for i, line in enumerate(f):
 10         content += line
 11         if (i+1) % n == 0:
 12             yield content
 13             content = b''
 14     else:
 15         # 存在最后不到 n行的数据不能被返回,这里返回一下
 16         yield content
 17 
 18 
 19 def send_file(dw_server, filename):
 20     """ 完成传输文件的功能"""
 21     f = None
 22     # 尝试打开文件
 23     try:
 24         f = open(filename, "rb")
 25     except Exception as e:
 26         print("没有该文件%s" % filename)
 27 
 28     # 当文件正常打开,发送数据
 29     if f:
 30         for i in read_data(f, 5):
 31             print("开始读入数据")
 32             # 回复给客户端的下载请求
 33             dw_server.send(i)
 34             time.sleep(0.1)
 35         else:
 36             # 循环正常结束,则执行关闭操作
 37             f.close()
 38         # 发送结束标志,说明数据传输完毕!
 39         dw_server.send(b'001')
 40     else:
 41         print("open files error")
 42 
 43 
 44 def server():
 45     # 1. 创建套接字
 46     tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 47     # 2. 绑定本地信息(ip,端口)
 48     tcp_socket.bind(("", 7890))
 49     # 3.设置为监听状态(监听套接字)
 50     tcp_socket.listen(128)
 51     print("我是服务器,我现在正在堵塞中,等待监听客户端请求。。。。")
 52 
 53     while True:
 54         # 4.等待客户端的链接(产生指定的接收套接字,服务该链接),采用阻塞的方式!
 55         client_socket, client_addr = tcp_socket.accept()
 56         print("我同意客户端的链接请求,下面是接受套接字为您服务。。。。")
 57         while True:
 58             dw_filename = client_socket.recv(1024).decode("utf-8")
 59             print(dw_filename)
 60             if dw_filename == "exit" or dw_filename == '':
 61                 print("服务结束")
 62                 break
 63             send_file(client_socket ,dw_filename)
 64         # 6.关闭套接字
 65         print("关闭客户端")
 66         client_socket.close()
 67     tcp_socket.close()
 68 
 69 
 70 if __name__ == "__main__":
 71     server()


结果展示:

  • 客户端:
    在这里插入图片描述

敬请期待下节,<<多线程版udp聊天室>> 以及 《多版本并发web服务器实现》及相关tcp可靠传输等网络知识记录

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值