Python笔记(四)网络编程

 

一、IP地址

 IP地址用来标记网络上的一台电脑,在本地局域网上是唯一的;

 

Linux中用ifconfig命令来查看IP地址,Windows中用ipconfig命令;

禁用网卡:sudo ifconfig ens33 down

开启网卡:sudo ifconfig ens33 up

 

IP地址的分类:IPV4、IPV6

每个IP地址由两个部分组成:网络地址、主机地址

A类IP地址

一个A类IP地址由1字节的网络地址和3字节主机地址组成,网络地址的最高位必须是“0”,

地址范围1.0.0.1-126.255.255.254

二进制表示为:00000001 00000000 00000000 00000001 - 01111110 11111111 11111111 11111110

可用的A类网络有126个,每个网络能容纳1677214个主机

B类IP地址

一个B类IP地址由2个字节的网络地址和2个字节的主机地址组成,网络地址的最高位必须是“10”,

地址范围128.1.0.1-191.255.255.254

二进制表示为:10000000 00000001 00000000 00000001 - 10111111 11111111 11111111 11111110

可用的B类网络有16384个,每个网络能容纳65534主机

C类IP地址

一个C类IP地址由3字节的网络地址和1字节的主机地址组成,网络地址的最高位必须是“110”

范围192.0.1.1-223.255.255.254

二进制表示为: 11000000 00000000 00000001 00000001 - 11011111 11111111 11111110 11111110

C类网络可达2097152个,每个网络能容纳254个主机

D类地址用于多点广播

D类IP地址第一个字节以“1110”开始,它是一个专门保留的地址。

它并不指向特定的网络,目前这一类地址被用在多点广播(Multicast)中

多点广播地址用来一次寻址一组计算机 s 地址范围224.0.0.1-239.255.255.254

E类IP地址

以“1111”开始,为将来使用保留

E类地址保留,仅作实验和开发用

私有ip

在这么多网络IP中,国际规定有一部分IP地址是用于我们的局域网使用,也就

是属于私网IP,不在公网中使用的,它们的范围是:

10.0.0.0~10.255.255.255

172.16.0.0~172.31.255.255

192.168.0.0~192.168.255.255
注意

IP地址127.0.0.1~127.255.255.255用于回路测试,

如:127.0.0.1可以代表本机IP地址,用http://127.0.0.1就可以测试本机中配置的Web服务器。

 

二、端口

标识主机中的应用进程。

 

端口是通过端口号来标记的,端口号只有整数,范围是从0到65535

注意:端口数不一样的*nix系统不一样,还可以手动修改

知名端口(Well Known Ports)

知名端口是众所周知的端口号,范围从0到1023

  80端口分配给HTTP服务

  21端口分配给FTP服务

一般情况下,如果一个程序需要使用知名端口的需要有root权限

动态端口(Dynamic Ports)

动态端口的范围是从1024到65535

之所以称为动态端口,是因为它一般不固定分配某种服务,而是动态分配。

动态分配是指当一个系统程序或应用程序程序需要网络通信时,它向主机申请一个端口,主机从可用的端口号中分配一个供它使用。

当这个程序关闭时,同时也就释放了所占用的端口号。

 

三、socket

socket(简称 套接字) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:

它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的

例如我们每天浏览网页、QQ 聊天、收发 email 等等

 

创建socket

import socket
socket.socket(AddressFamily, Type) 返回值是一个套接字对象
import socket

# 创建udp的套接字
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# ...这里是使用套接字的功能(省略)...

# 不用的时候,关闭套接字
s.close()
import socket

# 创建tcp的套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# ...这里是使用套接字的功能(省略)...

# 不用的时候,关闭套接字
s.close()

函数 socket.socket 创建一个 socket,该函数带有两个参数:

  • Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
  • Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)

 

四、UDP网络程序

User Datagram Protocol的简称, 中文名是用户数据报协议

主机使用网络调试助手接收消息,虚拟机网络需要设置为桥接模式(IP在一个网段)

我遇到的问题:

发消息:NAT模式也可以发消息,但因为主机防火墙没有关,虚拟机ping不通主机

收消息:改完桥接就断网,主机网络设置中两个VM的禁用就可以顺利改为桥接

def main():
    # 创建一个udp套接字
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 发送数据到指定电脑的指定程序中
    # b表示以字节bytes类型发送,否则会报错
    udp_socket.sendto(b"hahahah000000", ("192.168.0.105", 8080))

    # 关闭套接字
    udp_socket.close()


if __name__ == "__main__":
    main()

编码转换:

str->bytes:encode编码

bytes->str:decode解码

import socket


def main():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    data = input("需要发送的数据:")

    udp_socket.sendto(data.encode("utf-8"), ("192.168.0.105", 8080))

    udp_socket.close()


if __name__ == "__main__":
    main()

 循环发送数据:

import socket


def main():

    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    while True:

        data = input("请输入要传输的数据:")

        if data == "exit":
            break

        udp_socket.sendto(data.encode("utf-8"), ("192.168.0.105", 8080))

    udp_socket.close()


if __name__ == '__main__':
    main()

以上测试我主机的ip为192.168.0.105:8080,虚拟机IP为192.168.0.115:54321(NAT模式时为192.168.152.128 可以发送不能接收)

接收udp数据:

from socket import *

# 创建套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)

# 绑定本地的相关信息,如果一个网络程序不绑定,系统会随机分配
local_addr = ("", 54321)  # ip一般不用写,表示本机的任何一个ip
udp_socket.bind(local_addr)

# 接收数据
recv_data = udp_socket.recvfrom(1024)  # 1024表示本次接收的最大字节数
# recv_data中存储的是一个元组(接收的数据,(发送方的ip,发送方的端口))

# 接收中文需要解码:utf-8不能解码bytes,因为windows默认编码是gbk
print("%s : %s" % (recv_data[1], recv_data[0].decode("gbk")))

udp_socket.close()

 

发送数据的流程:

(若没有绑定端口,每一次系统随机分配的端口号不一样)

创建套接字

发送数据

关闭

 

接收数据的流程:

创建套接字

绑定IP和端口

接收数据

关闭

 

五、UDP聊天器

 单工:单向,如收音机

半双工:双向,同一时刻只能有一个方向,如对讲机

全双工:双向,同一时刻可以两方传输,如打电话

socket套接字是全双工;

import socket


def main():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    udp_socket.bind(('', 8765))

    while True:

        print("1.发送消息 2.接收消息 0.退出系统")
        op = input("请输入功能")

        if op == "1":
            send_mess = input("要发送的数据:")
            udp_socket.sendto(send_mess.encode('utf-8'), ('192.168.0.105', 8080))

        elif op == "2":
            recv_mess = udp_socket.recvfrom(1024)
            print("接收到的数据:%s" % recv_mess[0].decode('gbk'))

        elif op == "0":
            break

        else:
            print("输入有误")

    udp_socket.close()


if __name__ == "__main__":
    main()

 

六、TCP协议

TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。

TCP通信需要经过创建连接、数据传送、终止连接三个步骤。

TCP通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中,"打电话""

1. 面向连接

通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。

双方间的数据传输都可以通过这一个连接进行。

完成数据交换后,双方必须断开此连接,以释放系统资源。

这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。

2. 可靠传输

1)TCP采用发送应答机制

TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功

2)超时重传

发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。

TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。

3)错误校验

TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。

4) 流量控制和阻塞管理

流量控制用来避免主机发送得过快而使接收方来不及完全收下。

 

udp与tcp区别:

udp通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似于生活中,"写信""

udp通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中,"打电话""

 

 

七、TCP客户端

tcp的客户端要比服务器端简单很多,如果说服务器端是需要自己买手机、查手机卡、设置铃声、等待别人打电话流程的话,那么客户端就只需要找一个电话亭,拿起电话拨打即可,流程要少很多

import socket


def main():
    # 创建套接字
    tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 建立连接
    tcp_client.connect(('192.168.0.105', 8080))

    send_mess = input("要发送的数据:")

    # 发送消息
    tcp_client.send(send_mess.encode('utf-8'))

    # 接收消息
    recv_mess = tcp_client.recv(1024)

    print(recv_mess.decode('gbk'))

    # 关闭套接字
    tcp_client.close()


if __name__ == "__main__":
    main()

 

八、TCP服务器

在程序中,如果想要完成一个tcp服务器的功能,需要的流程如下:

  1. socket创建一个套接字
  2. bind绑定ip和port
  3. listen使套接字变为可以被动链接
  4. accept等待客户端的链接
  5. recv/send接收发送数据
import socket


def main():
    # 创建套接字(买手机
    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 绑定ip和端口(插手机卡
    tcp_server.bind(('', 7788))

    # 使套接字变为被动套接字,套接字默认是主动的(设为正常接听状态
    tcp_server.listen(128)

    # 等待客户端的链接,返回值是元组(等待电话打入
    # client_socket用来为这个客户端服务,tcp_server继续等待新客户端的连接
    client_socket, client_addr = tcp_server.accept()

    print(client_addr)

    # 接收消息
    recv_data = client_socket.recv(1024)
    print(recv_data.decode('gbk'))

    # 发送消息
    client_socket.send("hahahaha...".encode('utf-8'))

    # 关闭套接字
    client_socket.close()
    tcp_server.close()


if __name__ == "__main__":
    main()

循环为多个客户端服务:

import socket


def main():

    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    tcp_server.bind(('', 9876))

    tcp_server.listen(128)

    while True:
        print("等待一个新的客户端到来...")

        client_socket, client_addr = tcp_server.accept()

        recv_data = client_socket.recv(1024)

        print("来自%s : %s" % (client_addr, recv_data.decode('gbk')))

        client_socket.close()

        print("服务完毕。")

    tcp_server.close()


if __name__ == "__main__":
    main()

循环为多个客户端服务并多次服务一个客户端:

import socket


def main():

    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    tcp_server.bind(('', 7788))

    tcp_server.listen(128)

    while True:
        print("等待一个新的客户端到来...")

        client_socket, client_addr = tcp_server.accept()

        print("客户端%s:%s连接成功" % client_addr)

        while True:
            recv_data = client_socket.recv(1024)
            # 如果recv解堵塞,有两种可能:
            # 客户端发过来数据
            # 客户端调用了close(recv_data为空)
            if recv_data:
                print("来自%s : %s" % (client_addr, recv_data.decode('gbk')))
            else:
                break

        client_socket.close()

        print("-------服务完毕-------")

    tcp_server.close()


if __name__ == "__main__":
    main()

listen中的值代表多少个客户端可以同时链接

tcp注意点:

  1. tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器

  2. tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机

  3. tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的

  4. 当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信

  5. 当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务

  6. listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的

  7. 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信。

  8. 关闭accept返回的套接字意味着这个客户端已经服务完毕

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

 

九、文件下载器

 with as 操作文件

# 不用close
with open("文件名", "wb") as f:
    f.write("111222333".encode('gbk'))

# 相当于这种写法,即使出现异常,也会close
f = open("文件名", "w")
try:
    f.write("aabbcc")
except:
    f.close()

文件下载客户端

import socket


def main():
    # 创建套接字
    tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 链接服务器
    tcp_client.connect(("192.168.0.115", 9876))

    # 发送下载的文件名
    file_name = input("输入要下载的文件名:")
    tcp_client.send(file_name.encode('utf-8'))

    # 接收文件数据
    recv_data = tcp_client.recv(1024)

    # 将接收到的数据包存到文件
    if recv_data:
        with open("[接收]"+file_name, "wb") as f:
            f.write(recv_data)

    # 关闭套接字
    tcp_client.close()


if __name__ == "__main__":
    main()

文件下载服务器

import socket


def send_file(client_socket, client_addr):
    # 接收客户端需要下载的文件名
    file_name = client_socket.recv(1024)
    print("客户端%s需要下载的文件是:%s" % (client_addr, file_name.decode('utf-8')))

    file_content = None
    # 打开文件 读取数据
    # with的前提是打得开的前提下,处理读写异常,这里不能用with
    try:
        f = open(file_name, 'rb')
        file_content = f.read()
        f.close()
    except Exception as ret:
        print("没有要下载的文件%s:%s" % (file_name, ret))

    # 发送文件数据给客户端
    if file_content:
        client_socket.send(file_content)
        print("文件发送成功")


def main():
    # 创建套接字
    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 绑定ip和端口
    tcp_server.bind(('', 9876))

    # 开启监听
    tcp_server.listen(128)

    # 接收客户端连接
    client_socket, client_addr = tcp_server.accept()

    # 发送文件
    send_file(client_socket, client_addr)

    # 关闭套接字
    client_socket.close()
    tcp_server.close()


if __name__ == "__main__":
    main()

 

转载于:https://www.cnblogs.com/ysysyzz/p/11117517.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值