Python进阶之网络编程

网络通信

使用网络的目的

把多方链接在一起,进行数据传递;
网络编程就是,让不同电脑上的软件进行数据传递,即进程间通信;

ip地址

ip地址概念和作用

IP地址是什么:比如192.168.1.1 这样的一些数字;
ip地址的作用:用来在电脑中 标识唯一一台电脑,比如192.168.1.1;在本地局域网是唯一的。

网卡信息

查看网卡信息

Linux:ifconfig
windows:ipconfig

  • ensxx:用来与外部进行通信的网卡;
  • lo:环回网卡,用来进行本地通信的;

linux关闭/开启网卡:sudo ifconfig ensxx down/up

ip和ip地址的分类

ip分为ipv4和ipv6

ip地址分为:

  • A类地址
  • B类地址
  • C类地址
  • D类地址--用于多播
  • E类地址--保留地址,因ipv6诞生,已无用
  • 私有ip

单播--一对一
多播--一对多
广播--多对多

端口

ip:标识电脑;
端口:标识电脑上的进程(正在运行的程序);
ip和端口一起使用,唯一标识主机中的应用程序,进行统一软件的通信;

端口分类

知名端口

固定分配给特定进程的端口号,其他进程一般无法使用这个端口号;
小于1024的,大部分都是知名端口;
范围从0~1023;

动态端口

不固定分配,动态分配,使用后释放的端口号;
范围1024~65535;

socket

socket的概念

socket是进程间通信的一种方式,能实现不同主机间的进程间通信,即socket是用来网络通信必备的东西;

创建socket

创建套接字:

import socket
soc = socket.socket(AddressFamily, Type)

函数socket.socket创建一个socket,该函数有两个参数:
Address Family:可选 AF_INET(用于internet进程间通信)和AF_UNIX(用于同一台机器进程间通信);
Type:套接字类型,可选 SOCK_STREAM(流式套接字,主用于TCP协议)/SOCK_DGRAM(数据报套接字,主用于UDP套接字);

创建tcp套接字

import socket

soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
...
soc.close()

创建udp套接字

import socket

soc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
...
soc.close()

udp

udp使用socket发送数据

在同一局域网内发消息;
如果用虚拟机和windows,要用桥接模式,确保在同一局域网内;

import socket

def main():
    # 创建一个udp套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 使用套接字收发数据
    udp_socket.sendto(b"hahaha", ("193.168.77.1", 8080))
    # 关闭套接字
    udp_socket.close()

if __name__ == "__main__":
    main()

udp发送数据的几种情况:

  1. 在固定数据的引号前加b,不能使用于用户自定义数据;
  2. 用户自定义数据,并进行发送,使用.encode("utf-8")进行encode编码
  3. 用户循环发送数据
  4. 用户循环发送数据并可以退出

只贴出最后一种情况,即完整代码

import socket

def main():
    # 创建一个udp套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    while 1:
        # 从键盘获取要发送的数据
        send_data = input("请输入你要发送的数据:")
        if send_data == "exit":
            break
        # 使用套接字收发数据
        udp_socket.sendto(send_data.encode("utf-8"), ("193.168.77.1", 8080))

    # 关闭套接字
    udp_socket.close()

if __name__ == "__main__":
    main()

udp接收数据

接收到的数据是一个元组,元组第一部分是发送方发送的内容,元组第二部分是发送方的ip地址和端口号;

import socket

def main():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    localaddr = ('', 8080)
    udp_socket.bind(localaddr)  # 必须绑定自己电脑的ip和端口

    # 接收数据
    recv_data = udp_socket.recvfrom(1024)
    # recv_data这个变量存储的是一个元组,例如 (b'hahaha', ('192.168.77.1', 8888))
    recv_msg = recv_data[0]
    send_addr = recv_data[1]
    # print("%s 发送了:%s" % (str(send_addr), recv_msg.decode("utf-8")))  # linux发送的数据用utf8解码
    print("%s 发送了:%s" % (str(send_addr), recv_msg.decode("gbk")))  # windows发送的数据用gbk解码

    udp_socket.close()

if __name__ == "__main__":
    main()

udp接发数据总结

发送数据的流程:

  1. 创建套接字
  2. 发送数据
  3. 关闭套接字

接收数据的流程:

  1. 创建套接字
  2. 绑定本地自己的信息,ip和端口
  3. 接收数据
  4. 关闭套接字

端口绑定的问题

  • 如果在你发送数据时,还没有绑定端口,那么操作系统就会随机给你分配一个端口,循环发送时用的是同一个端口;
  • 也可以先绑定端口,再发送数据。

udp发送消息时自己绑定端口示例

import socket

def main():
    # 创建一个udp套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 绑定端口
    udp_socket.bind(('192.168.13.1', 8080))
    while 1:
        # 从键盘获取要发送的数据
        send_data = input("请输入你要发送的数据:")
        if send_data == "exit":
            break
        # 使用套接字收发数据
        udp_socket.sendto(send_data.encode("utf-8"), ("193.168.77.1", 8080))

    # 关闭套接字
    udp_socket.close()  # 按ctrl+c退出

if __name__ == "__main__":
    main()

但应注意,同一端口在同一时间不能被两个不同的程序同时使用

单工,半双工,全双工

单工半双工全双工的理解

单工:
只能单向发送信息,别人接收,别人不能回复消息,比如广播;

半双工:
两个人都能发消息,但是在同一时间只能有一个人发消息,比如对讲机;

全双工
两个人都能发消息,能同时发,比如打电话;

udp使用同一套接字收且发数据

"""socket套接字是全双工"""
import socket

def main():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_socket.bind(('192.168.13.1', 8080))
    # 让用户输入要发送的ip地址和端口
    dest_ip = input("请输入你要发送数据的ip地址:")
    dest_port = int(input("请输入你要发送数据的端口号:"))

    # 从键盘获取要发送的数据
    send_data = input("请输入你要发送的数据:")
    # 使用套接字收发数据
    udp_socket.sendto(send_data.encode("utf-8"), (dest_ip, dest_port))
    # 套接字可以同时 收发数据;
    recv_data = udp_socket.recvfrom(1024)
    print(recv_data)

    # 关闭套接字
    udp_socket.close()  # 按ctrl+c退出

if __name__ == "__main__":
    main()

在这里体现不出来socket是全双工,因为现在解释器只能按照流程,一步一步走下去,后面学习了进程线程协程就可以做到了。

tcp

tcp-可靠传输

tcp采取的机制

  1. 采用发送应答机制
  2. 超时重传
  3. 错误校验
  4. 流量控制和阻塞管理

tcp与udp的区别

  1. tcp更安全可靠,udp相对没那么安全可靠;
  2. 面向连接
  3. 有序数据传输
  4. 重发丢失的数据
  5. 舍弃重复的数据包
  6. 无差错的数据传输
  7. 阻塞/流量控制

tcp,udp应用场景

tcp应用场景:下载,发送消息
udp应用场景:电话,视频直播等

tcp客户端

tcp客户端发送数据

import socket

def main():
    # 1.创建tcp的套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.链接服务器
    tcp_socket.connect(('193.168.11.1', 8080))
    # 3.发送/接收消息
    send_data = input("请输入你要发送的消息:")
    tcp_socket.send(send_data.encode("utf-8"))
    # 4.关闭套接字
    tcp_socket.close()

if __name__ == "__main__":
    main()

tcp服务器

监听套接字,专门用来监听的;
accept会对应新创建的套接字,当监听套接字收到一个请求后,将该请求分配给新套接字,由此监听套接字可以继续去监听了,而新套接字则为该胡克段服务。

import socket

def main():
    # 创建tcp套接字
    tcp_service_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_service_socket.bind(('', 8080))
    # 让默认的套接字由主动变为被动
    tcp_service_socket.listen(128)

    # 等待客户端的链接
    new_client_socket, client_addr = tcp_service_socket.accept()
    print("链接的客户端地址为:", client_addr)
    # 接收客户端发送过来的请求
    recv_data = new_client_socket.recvfrom(1024)
    print(recv_data)
    # 给客户端回送消息
    new_client_socket.send("hahahah".encode("utf-8"))

    new_client_socket.close()
    tcp_service_socket.close()

if __name__ == '__main__':
    main()

listen里面的参数,表示同时只允许128个链接访问。

QQ不绑定端口的运行原理-扩展

udp和tcp并用;
使用QQ,先登录,登录后告诉腾讯服务器此QQ运行的端口,发消息时,通过腾讯服务器转发给另一个QQ;
不绑定端口也有一个好处,就是允许多开,即一个电脑上可以运行多个QQ;

recv和recvfrom的区别

recvfrom里面不仅有发过来的数据,还有发过来数据的人的信息;
recv里面就只有数据;

tcp客户端服务端流程梳理

tcp服务器流程梳理

  1. 创建服务器套接字
  2. 绑定本地信息
  3. 让默认的套接字由主动变为被动
  4. 等待客户端的链接,堵塞
  5. 被客户端链接后,创建一个新的客服套接字为客户端服务;
  6. 接收客户端发送的消息,堵塞
  7. 接收客户端发送的消息后,给客户端回消息
  8. 关闭客服套接字,关闭服务端套接字

tcp注意点

  1. tcp服务器一般情况下都需要綁定,否则客户端找不到这个服务器。
  2. tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip, port等信息就好,本地客户端可以随机。
  3. tcp服务器通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的。
  4. 当客户端需要链接服务器时,就需要使用connect进行链接, udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信。
  5. 当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务。
  6. liston后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的。
  7. 关闭isten后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信。
  8. 关闭accept返回的套接字意味着这个客户端已经服务完毕。

9.当客户端的套接字调用close后.服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过 返回数据的长度来区别客户端是否已经下线。

tcp应用案例

示例1-为一个用户办理一次业务:

"""可以理解为银行一个客服为排队的人员办理业务"""

import socket

def main():
    # 1.创建tcp套接字
    tcp_service_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.绑定本地信息
    tcp_service_socket.bind(('', 8080))

    # 3.让默认的套接字由主动变为被动
    tcp_service_socket.listen(128)
    while 1:
        # 4.等待客户端的链接
        new_client_socket, client_addr = tcp_service_socket.accept()
        print("链接的客户端地址为:", client_addr)
        # 接收客户端发送过来的请求
        recv_data = new_client_socket.recvfrom(1024)
        print(recv_data)
        # 给客户端回送消息
        new_client_socket.send("hahahah".encode("utf-8"))
        # 关闭套接字
        new_client_socket.close()

    tcp_service_socket.close()

if __name__ == '__main__':
    main()

示例2-为同一用户服务多次并判断一个用户是否服务完毕:

"""可以理解为银行一个客服为排队的人员办理业务"""

import socket

def main():
    # 1.创建tcp套接字
    tcp_service_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.绑定本地信息
    tcp_service_socket.bind(('', 8080))

    # 3.让默认的套接字由主动变为被动
    tcp_service_socket.listen(128)
    while 1:
        # 4.等待客户端的链接
        new_client_socket, client_addr = tcp_service_socket.accept()
        print("链接的客户端地址为:", client_addr)
        # 循环目的:为同一个客户服务多次
        while 1:
            # 接收客户端发送过来的请求
            recv_data = new_client_socket.recvfrom(1024)
            print(recv_data)
            # 如果recv解堵塞,那么有两种方式
            # 1.客户端发了数据过来
            # 2.客户端调用了close
            if recv_data:
                # 给客户端回送消息
                new_client_socket.send("hahahah".encode("utf-8"))
            else:
                break
        # 关闭套接字
        new_client_socket.close()

    tcp_service_socket.close()

if __name__ == '__main__':
    main()

示例3-tcp文件下载客户端和服务端:

文件下载客户端

import socket

def main():
    # 1.创建套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.获取服务器的ip,port
    dest_ip = input("请输入你要链接的服务器ip:")
    dest_port = input("请输入你要链接的端口:")
    # 3.链接服务器
    tcp_socket.connect((dest_ip, dest_port))

    # 4.获取下载的文件名字
    want_file = input("请输入你要下载的文件:")
    # 5.将文件名字发送到服务器
    tcp_socket.send(want_file.encode("utf-8"))

    # 6.接收要下载的文件
    file_data = tcp_socket.recv(1024)
    # 7.将接收文件的数据写入一个文件中
    if file_data:
        with open("[复件]" + want_file, "wb") as f:
            f.write(file_data)

    # 8.关闭套接字
    tcp_socket.close()
    pass

if __name__ == '__main__':
    main()

文件下载服务端

import socket

def send_file2client(new_socket, client_addr):
    # 1.接受客户端发送过来的 要下载的文件名
    want_file = new_socket.recv(1024).decode("utf-8")
    print("客户端 %s 要接收的文件为:%s" % (str(client_addr), want_file))
    # 2.读取文件数据
    file_data = None
    try:
        f = open(want_file, "rb")
        file_data = f.read()
        f.close()
    except Exception as e:
        print("你要下载的文件 %s 不存在" % want_file)

    # 3.发送文件的数据给客户端
    if file_data:
        new_socket.send(file_data)

def main():
    # 1.创建套接字
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 2.绑定本地信息
    tcp_socket.bind(('', 8080))
    # 3.套接字被动接受 listen
    tcp_socket.listen(128)
    while 1:
        # 4.等待客户端的链接 accept
        new_socket, client_addr = tcp_socket.accept()
        # 5.调用函数发送文件到客户端
        send_file2client(new_socket, client_addr)
        # 7.关闭套接字
        new_socket.close()

    tcp_socket.close()

if __name__ == '__main__':
    main()

原文地址:https://www.cnblogs.com/yifchan/p/python-1-22.html

时间: 2024-11-29 09:12:24

Python进阶之网络编程的相关文章

python高级之网络编程

python高级之网络编程 本节内容 网络通信概念 socket编程 socket模块一些方法 聊天socket实现 远程执行命令及上传文件 socketserver及其源码分析 1.网络通信概念 说到网络通信,那就不得不说TCP/IP协议簇的OSI七层模型了,这个东西当初在学校都学烂了...(PS:毕竟本人是网络工程专业出身...) 简单介绍下七层模型从底层到上层的顺序:物理层(定义物理设备的各项标准),数据链路层(mac地址等其他东西的封装),网络层(IP包头的的封装),传输层(TCP/UD

一、Python 进阶 之 函数式编程

Python 进阶 之 函数式编程 写在前面 入门阶段的系列笔记传送门 → 进这里 已经掌握了基础的内容如下: 变量和数据类型:Python 内置的基本类型 List 和 Tuple:顺序的集合类型 条件判断和循环:控制程序流程 Dict 和 Set:根据Key访问的集合类型 函数:定义和调用函数 切片:如何对 list 进行切片 迭代:如何用 for 循环迭代集合类型 列表生成式:如何快速生成列表 接下来我要学会: 函数式编程 如何使用 Python 的模块(内置模块和第三方模块) 面向对象编

Python四大主流网络编程框架

目前Python的网络编程框架已经多达几十个,逐个学习它们显然不现实.但这些框架在系统架构和运行环境中有很多共通之处,本文带领读者学习基于Python网络框架开发的常用知识,及目前的4种主流Python网络框架:Django.Tornado.Flask.Twisted. 网络框架及MVC架构 所谓网络框架是指这样的一组Python包,它能够使开发者专注于网站应用业务逻辑的开发,而无须处理网络应用底层的协议.线程.进程等方面.这样能大大提高开发者的工作效率,同时提高网络应用程序的质量. 在目前Py

Python TCP通信网络编程

最近在看廖雪峰老师的基础教程(http://www.liaoxuefeng.com/),今天实现了一下简单Python的Socket的网络编程. 1. Socket网络编程 Socket是网络编程的一个抽象概念.通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可. 2. 客户端 大多数连接都是可靠的TCP连接.创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器.举个例子,当我们在浏览器中访问新浪时,我

Python实践之网络编程1-简单的网络请求程序

在了解python基础的语法基础上,就可以自由的去组合出自己需要各类高级功能. 由于python语言的特性,各类功能的实现都会非常快捷的. 网络变成就是python具备的高级特性之一. 需要进行网络编程首先需要了解几个模块urllib和urllib2 1.简单的访问请求 import sys,urllib,urllib2 url=input("please input the url:") url = "http://mail.126.com" #发起请求 req

python之路 -- 网络编程

1.软件开发的架构 - C/S架构(需要安装应用程序使用的软件) c client 客户端 s server 服务端 - B/S架构(可以通过浏览器使用的) b broser 浏览器 s server 服务端 不需要额外的安装客户端了,只需要一个网址就可以访问 轻量级,使用成本低 2.tcp协议/udp协议 tcp协议 全双工的通信协议 建立了专门穿送数据的通道(连接),是一个长连接 面向流的传输 传输速率比udp协议慢 数据安全不容易丢失 大文件算法自己拆包编号发送 建立连接的 三次握手 断开

python学习之网络编程基础

引入场景:客户与银行关系 银行职员负责给客户提供取钱服务,客户通过账户密码跟银行职员建立合作关系.此时银行职员就可以作为服务器,当用户A取完钱后他需要等待下一个用户的接入,用户的账号密码就是建立合作关系的凭据.------简单的客户端/服务器架构模型. 客户端/服务器网络编程过程 一:创建套接字(通信端点) AF_XXX解释:地址家族名称,AF:Address Family 基于文件套接字 AF_UNIX 基于网络套接字 AF_INET 代表ipv4  (python网络编程中常用的套接字)  

『Python』socket网络编程

Python3网络编程 '''无论是str2bytes或者是bytes2str其编码方式都是utf-8 str( ,encoding='utf-8') bytes( ,encoding='utf-8') 而在使用.encode('utf-8')时,虽然type类型是byte,但常常报错''' Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯. socket起源于UNIX,在Unix一切皆文

Python进阶之函数式编程(把函数作为参数)

什么是函数式编程? 什么是函数式编程? 函数:function 函数式:functional,一种编程范式 函数式编程是一种抽象计算的编程模式 函数≠函数式,比如:计算≠计算机 在计算机当中,计算机硬件是最底层的,而越往上语言越高级 低--------------------------------->高计算机硬件-->汇编语言-->c语言-->Python语言 ↓ ↓ ↓ 指令 函数 函数式计算机------------------------>计算(数学) ○ 函数式编程