Python 网络编程

网络编程:
自从互联网诞生以来,现在基本上所有的程序都是网络程序,很少有单机版的程序了。
计算机网络就是把各个计算机连接到一起,让网络中的计算机可以互相通信。
网络编程就是如何在程序中实现两台计算机的通信。
举个例子,当你使用浏览器访问新浪网时,你的计算机就和新浪的某台服务器通过互联网连接起来了,然后,新浪的服务器把网页内容作为数据通过互联网传输到你的电脑上。
网络通信是两台计算机上的两个进程之间的通信。
由于你的电脑上可能不止浏览器,还有QQ、Skype、Dropbox、邮件客户端等,不同的程序连接的别的计算机也会不同,所以,更确切地说,网络通信是两台计算机上的两个进程之间的通信。比如,浏览器进程和新浪服务器上的某个Web服务进程在通信,而QQ进程是和腾讯的某个服务器上的某个进程在通信。
网络编程对所有开发语言都是一样的,Python也不例外。用Python进行网络编程,就是在Python程序本身这个进程内,连接别的服务器进程的通信端口进行通信。
在这里插入图片描述1 什么是协议

计算机为了联网,就必须规定通信协议,早期的计算机网络,都是由各厂商自己规定一套协议,IBM、Apple和Microsoft都有各自的网络协议,互不兼容,这就好比一群人有的说英语,有的说中文,有的说德语,说同一种语言的人可以交流,不同的语言之间就不行了。
为了把全世界的所有不同类型的计算机都连接起来,就必须规定一套全球通用的协议,为了实现互联网这个目标,就需要使用互联网协议簇(Internet Protocol Suite)就是通用协议标准。
Internet是由inter和net两个单词组合起来的,原意就是连接“网络”的网络,有了Internet,任何私有网络,只要支持这个协议,就可以联入互联网。
因为互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议。
2 TCP/IP协议族

TCP/IP提供点对点的链接机制,将数据应该如何封装、定址、传输、路由以及在目的地如何接收,都加以标准化。
它通常将软件通信过程抽象化为四个抽象层,采取协议堆栈的方式,分别实现出不同通信协议。协议套组下的各种协议,依其功能不同,被分别归属到这四个层次结构之中,常被视为是简化的七层OSI模型。
OSI模型,即开放式通信系统互联参考模型(Open System Interconnection Reference Model),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。

在这里插入图片描述

链路层:处理与电缆或其他传输媒介的物理接口
网络层:处理数据在网络中的活动

ip协议【网络互连协议】
用途:将多个包在网络中联系起来,传输数据包(不可靠传输),最基本功能就是寻址和分段功能,不提供端到端,路由到路由的确认,不提供重发和流量控制。是计算机网络能够相互通信的基本规则。出错则像ICMP报告,ICMP在IP模块中实现ICMP协议
用途:面向无连接协议,用于传输错误报告控制信息(控制信息是指网络不通畅,主机是否到达,路由是否可用的这些网络本身的消息,不涉及用户传输的数据)

ARP协议->地址解析协议
用途:根据IP地址获取物理地址的协议(即MAC地址)。在同一子网内通过ARP协议可以实现数据包的互相传递。不在一个子网内则无法获得MAC地址,只有通过网关去处理。

RARP协议->反转地址协议
用途:和ARP协议相反,将主机的物理地址转换成IP地址。
传输层:提供两台主机间端到端的通信

TCP协议->传输控制协议
用途:面向连接、保证高可靠性(数据无丢失、数据无失序、数据无错误、数据无重复到达)传输层协议。

UDP协议->用户数据协议
用途:无连接,不保证可靠的传输层协议。

应用层:用于不同的应用程序
NAT协议->网络地址转换协议【放在这里不完全合适】
用途:实现内网IP地址和公网地址之间的相互转换。将大量的内网IP转换成一个或者少量的公网IP

FTP协议->文件传输协议
用途:通过FTP协议在FTP客户端访问FTP服务端,默认使用20和21端口,20用于传输数据,21用于传输控制信息。

HTTP协议->超文本传输协议
用途:是用于从WWW服务器传输超文本到本地浏览器的传输协议。是客户端浏览器或其他程序与Web服务器之间的应用层通信协议。

TELNET协议
用途:是Internet远程登陆服务的标准协议和主要方式,为用户提供了在本地计算机上完成远程主机工作的能力。

SMTP协议->简单邮件传输协议
用途:控制邮件传输的规则,以及邮件的中转方式。

DNS协议
用途:定义域名规则,将域名和IP相互映射

在这里插入图片描述

3.IP地址

一个程序如何在网络上找到另一个程序?

首先,程序必须要启动,其次,必须有这台机器的地址,我们都知道我们人的地址大概就是国家\省\市\区\街道\楼\门牌号这样字。那么每一台联网的机器在网络上也有自己的地址,它的地址是怎么表示的呢?

就是使用一串数字来表示的,例如:100.4.5.6

IP地址是指互联网协议地址(英语:Internet Protocol Address,又译为网际协议地址),是IP Address的缩写。IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。

IP地址是一个32位的二进制数,通常被分割为4个“8位二进制数”(也就是4个字节)。IP地址通常用“点分十进制”表示成(a.b.c.d)的形式,其中,a,b,c,d都是0~255之间的十进制整数。例:点分十进IP地址(100.4.5.6),实际上是32位二进制数(01100100.00000100.00000101.00000110)。

4.端口

那么TCP/IP协议中的端口指的是什么呢?
端口就好一个房子的门,是出入这间房子的必经之路。

端口号
端口是通过端口号来标记的,端口号只有整数,范围是从0到65535(2的16次方)
系统预留端口号
预留端口号是广泛使用的端口号,方位从0-1023 开发中不要使用
如http的80端口 ftp的21端口

总结
通过IP在网络内寻找主机、通过端口寻找主机中的应用程序
即:ip地址精确到具体的一台电脑,而端口精确到具体的程序。

socket:
本地的进程间通信有很多种方式,例如队列、同步(互斥锁、条件变量等)
以上通信方式都是在一台机器上不同进程之间的通信方式,那么问题来了网络中进程之间如何通信?

网络中进程之间如何通信:
主要解决的问题是如何唯一标识一个进程,否则通信无从谈起!
在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。
其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。
这样利用ip地址,协议,端口就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互

什么是socket
socket(简称套接字) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:
它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于Socket来完成通信的。
例如我们每天浏览的、QQ 聊天、收发Email等等

创建socket
在Python中使用socket模块的函数socke就可以完成:socket.socket(AddressFamily, Type)
说明:
函数socket.socket创建一个socket,返回该 socket的描述符,该函数带有两个参数:
Address Family:可以选择AF_INET(用于 Internet 进程间通信)或者AF_UNIX(用于同一台机器进程间通信)实际工作中常用AF_INET
Type:套接字类型,可以是SOCK_STREAM(流式套接字,主要用于TCP协议)或者SOCK_DGRAM(数据报套接字,主要用于UDP协议)

eg:

import socket

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print("socketCreated")
print(s)

s2 = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
print("socketCreated")
print(s2)

常用方法
socket套接字常用操作
s.bind() 绑定(主机名称、端口)到一个套接字上
s.listen() 设置并启动TCP监听
s.accept() 等待客户端连接
s.connect() 连接指定服务器
s.recv() 接受TCP消息
s.send() 发送TCP消息
s.recvfrom() 接受UDP消息
s.sendto() 发送UDP消息
s.close() 关闭套接字对象

UDP编程

TCP是建立可靠连接,并且通信双方都可以以流的形式发送数据。相对TCP,UDP则是面向无连接的协议。
使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。
虽然用UDP传输数据不可靠,但它的优点是和TCP比,速度快,对于不要求可靠到达的数据,就可以使用UDP协议。

适用情况
UDP是面向消息的协议,通信时不需要建立连接,数据的传输当然是不可靠的,UDP一般用于多点通信和实时的数据业务,例如:
语音广播、视频、QQ、TFTP(简单文件传送)、SNMP(简单网络管理协议)、RIP(路由信息协 议,如:报告股票市场,航空信息)
DNS(域名解释)
在这里插入图片描述
服务端实现:

import socket

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

s_addr = ("127.0.0.1",8080)

server_socket.bind(s_addr)
print("服务器连接成功!")
while True:
    data,addr = server_socket.recvfrom(1024)
    print(data.decode("utf-8"))

server_socket.close()

客户端实现:

import socket

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

addr = ("127.0.0.1",8080)

message = "Good morning! Saturday!!!"


client_socket.sendto(message.encode("utf-8"),addr)

client_socket.close()

TCP编程
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
TCP通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中,“打电话”"
在这里插入图片描述

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

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

三次握手,四次挥手
在这里插入图片描述

TCP 连接三次握手
TCP 三次握手就好比两个人在街上隔着50米看见了对方,但是因为雾霾等原因不能100%确认,所以要通过招手的方式相互确定对方是否认识自己。
张三首先向李四招手(syn),李四看到张三向自己招手后,向对方点了点头挤出了一个微笑(ack)。张三看到李四微笑后确认了李四成功辨认出了自己(进入estalished状态)。
但是李四还有点狐疑,向四周看了一看,有没有可能张三是在看别人呢,他也需要确认一下。
所以李四也向张三招了招手(syn),张三看到李四向自己招手后知道对方是在寻求自己的确认,于是也点了点头挤出了微笑(ack),李四看到对方的微笑后确认了张三就是在向自己打招呼(进入established状态)。
我们看到这个过程中一共是四个动作,张三招手–李四点头微笑–李四招手–张三点头微笑。
其中李四连续进行了2个动作,先是点头微笑(回复对方),然后再次招手(寻求确认),实际上可以将这两个动作合一,招手的同时点头和微笑(syn+ack)。
于是四个动作就简化成了三个动作,张三招手–李四点头微笑并招手–张三点头微笑。这就是三次握手的本质,中间的一次动作是两个动作的合并。
TCP数据传输
TCP 数据传输就是两个人隔空对话,差了一点距离,所以需要对方反复确认听见了自己的话。

在这里插入图片描述

张三喊了一句话(data),李四听见了之后要向张三回复自己听见了(ack)。
如果张三喊了一句,半天没听到李四回复,张三就认为自己的话被大风吹走了,李四没听见,所以需要重新喊话,这就是tcp重传。
TCP 四次挥手
TCP断开链接的过程和建立链接的过程比较类似,只不过中间的两部并不总是会合成一步走,所以它分成了4个动作,张三挥手(fin)——李四伤感地微笑(ack)——李四挥手(fin)——张三伤感地微笑(ack)。

服务端实现:

import socket
#socket创建套接字
server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s_addr = ("127.0.0.1",8083)
#bind绑定ip和端口
server_socket.bind(s_addr)
#listen控制最大监听数
server_socket.listen(100)
# accept等待客户端的连接
client_socket, c_addr = server_socket.accept()

while True:
    print(client_socket,c_addr)
    print("连接成功!")
    data = client_socket.recv(1024)
    msg = data.decode("utf-8")
    print("接收到的消息为:",msg)

    message = input("请输入信息:")
    bytes = message.encode("utf-8")
    client_socket.send(bytes)
    print("发送消息成功!")


server_socket.close()

客户端实现:

import socket

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

c_addr = ("127.0.0.1",8083)

client_socket.connect(c_addr)

print("连接成功!")

while True:
    message = input("请输入信息:")
    bytes = message.encode("utf-8")
    client_socket.send(bytes)
    print("发送信息成功!")

    data = client_socket.recv(1024)
    msg = data.decode("utf-8")
    print("接收到的消息为:",msg)

client_socket.close()

多进程、多线程网络通信
服务端实现:

import socket
    from multiprocessing import Process
    s_addr = ("127.0.0.1",8084)
    def handle_client(client_socket):
        try:
            data = client_socket.recv(1024)
            print("接收到的消息为:", data.decode("utf-8"))
        except Exception as e:
            print(e)
    

if __name__ == '__main__':
    server_socket = None
    try:
        server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        server_socket.bind(s_addr)
        server_socket.listen(100)
        print("服务器创建连接成功!")
        while True:
            client_socket, addr = server_socket.accept()
            print(client_socket, addr)
            p = Process(target=handle_client,args=(client_socket,))
            p.start()
    except Exception as e:
        print("服务器连接失败")
    finally:
        if server_socket != None:
            server_socket.close()

客户端1实现:

import socket
client_socket = None
try:

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

c_addr = ("127.0.0.1",8084)

client_socket.connect(c_addr)
while True:
    message  = input("请输入信息:")
    bytes = message.encode("utf-8")
    client_socket.send(bytes)
    print("发送信息成功!")

    # data = client_socket.recv(1024)
    # msg = data.decode("utf-8")
    # print("接收到的信息为:",msg)
except Exception as e:
    print(e)

finally:
    if client_socket != None:
        client_socket.close()

客户端2实现:

import socket
client_socket = None
try:

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

    c_addr = ("127.0.0.1",8084)

    client_socket.connect(c_addr)
    while True:
        message  = input("请输入信息:")
        bytes = message.encode("utf-8")
        client_socket.send(bytes)
        print("发送信息成功!")

        data = client_socket.recv(1024)
        msg = data.decode("utf-8")
        print("接收到的信息为:",msg)
except Exception as e:
    print(e)

finally:
    if client_socket != None:
        client_socket.close()

tcp通信
服务端实现:

import socket,threading
server_socket = None
users = dict()  # 建立一个用户空字典


def add_user(client, addr):
    global users
    users[addr] = client
    print("添加新用户")


def func(client, addr):
    while True:
        try:
            message = client.recv(1024)
            for key, val in users.items():
                if key != addr:
                    val.send(message)
        except Exception as e:
            # raise e
            print("发送消息失败", e)


if __name__ == '__main__':

    try:
        server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s_addr = ("127.0.0.1",8055)
        server_socket.bind(s_addr)
        server_socket.listen(2)
        print("服务器创建连接成功!")

        for i in range(2):
            client, addr = server_socket.accept()
            add_user(client, addr)  # 将新用户添加到在线用户列表
            t = threading.Thread(target=func, args=(client, addr))
            t.start()
        input()

    except Exception as e:
        print("服务器连接失败",e)
    finally:
        if server_socket is not None:
            server_socket.close()

客户端实现:

import socket,threading

c_addr = ("127.0.0.1",8055)



def menu():
    menu = """
    请选择你要的操作:\n
    【1】选择I输入信息\n
    【2】选择O查看信息\n
    """
    select  =input(menu)
    return select

def send_msg(client_socket):
    try:
        msg = input("请输入内容:")
        client_socket.send(msg.encode("utf-8"))
    except Exception as e:
        print("发出错误信息",e)

msg_box = []
lock = threading.Lock()
def func(client_socket):
    try:
        while True:
            data = client_socket.recv(1024)
            msg = data.decode("utf-8")
            lock.acquire()
            msg_box.append(msg)
            lock.release()
    except Exception as e:
        print(e)



def show_receie_msgs():
    lock.acquire()
    while len(msg_box) != 0:
        e =msg_box.pop(0)
        print(e)
    lock.release()

if __name__ == '__main__':

    client_socket = None
    try:
        client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        client_socket.connect(c_addr)

        t_receiver = threading.Thread(target=func,args=(client_socket,))
        t_receiver.start()

        while True:
            select = menu()
            if select == "I":
                send_msg(client_socket)
            elif select == "O":
                show_receie_msgs()
            else:
                print("选项不正确!")
    except Exception as e:
        print("连接服务器失败",e)
    finally:
        client_socket.close()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值