Python—socket编程

2021-07-20 16:14:42 浏览数 (1)

一、何为socket编程

应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字 (Socket)的接口,区分不同应用程序进程间的网络通信和连接。

生成套接字的两个参数,一个是选择IP协议,另一个是选择UDP或者是TCP.

代码语言:javascript复制
# 创建一个udp套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#创建一个套接字, family=AF_INET type=NI_DGRAM
# 创建一个TCP套接字
tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

这里family的意思是选择哪种协议,AF_INET代表的是IPv4,SOCK_DGRAM创建的是UDP协议。

二、UDP套接字的收发流程

1.创建一个UDP套接字

2.套接字收发数据

3.关闭套接字

例子的代码如下:(需要打开网路调试助手进行辅助)

代码语言:javascript复制
def main():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    #创建一个套接字, family=2 即family=AF_INET type=16 即type=NI_DGRAM
    send_content = 666
    # 发送数据的内容
    # send_content = send_content.encode('gbk')
    host_addr = ("169.254.190.219", 8080)
    udp_socket.sendto(send_content, host_addr)
    udp_socket.close()


# 套接字发送数据
def main():
    udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    desc_addr = ("169.254.190.219", 8080)
    udp_socket.bind(("", 9999))
    while True:
        send_content = input("请输入要发送的数据:")
        if send_content == "q":
            break
        send_content=send_content.encode('gbk')
        udp_socket.sendto(send_content, desc_addr)


# 套接字接收数据
def main():
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    rec_addr =  ("169.254.190.219", 9999)
    udp_socket.bind(rec_addr)
    rec_data = udp_socket.recvfrom(1024)
    print(rec_data)
    rec_content = rec_data[0].decode('gbk')
    print("接收的内容为:{}n发送地址为:{}  端口为:{} ".format(rec_content,rec_data[1][0],rec_data[1][1]))


    udp_socket.close()
# 套接字无限循环接收消息
def main():
    udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    rec_addr = ("169.254.190.219", 9999)
    udp_socket.bind(rec_addr)
    while True:
        rec_data = udp_socket.recvfrom(1024)
        rec_content = rec_data[0].decode('gbk')
        if rec_content == "再见":
            break
        print("接收的内容为:{}n发送地址为:{}  端口为:{} ".format(rec_content,rec_data[1][0],rec_data[1][1]))


if __name__ == "__main__":
    main()

在这里有几个注意事项:

1.在发送中文的时候,我们应该使用encode(“gbk”)进行编码,而不使用utf-8的原因是Windows默认的字体编码是GBK模式。

2.在接收中文的时候也应该使用decode进行解码。

3.发送的数据如果没有编码的话,默认只能发送字节类型的数据。

三、如何使用UDP套接字又接收又发送消息呢?

如果我们想要使用udp套接字又发送消息又接收消息的话,我们可以定义一个发送消息的函数和一个接收消息的函数,其余部分可以在主函数当中完成。

首先,我们需要绑定一个ip地址发送数据,其次,我们还需要一个IP地址来接收发送的数据。当然,聊天器的版本并不高,返回的值也是我们所输入的值,当然,如果箱套聊天器变得更加有趣,我们可以对其进行进行升级。这个时候其实只要加入一些判断语句就可以实现哦。比如,我们可以在输入全部都是英文的时候发送“乖乖,你发的都是些什么内容呀,我的智商太低完全看不懂哦”

代码语言:javascript复制
import socket


'''
这是一个自己跟自己聊天的聊天器
第一版:
1.要绑定一个ip地址发送数据
2.要绑定一个ip地址来接收发送的数据
'''
def sendaddr(udp_socket):
    send_addr = ("169.254.190.219",9999)
    # udp_socket.bind(send_addr)
    send_content = input("请输入要输入的内容:")
    udp_socket.sendto(send_content.encode('gbk'),send_addr)


def recaddr(udp_socket):
    rec_data = udp_socket.recvfrom(1024)
    rec_content = rec_data[0].decode('gbk')
    if rec_content == "exit":
        exit()
    print("接收的内容为:{}n发送地址为:{}  端口为:{} ".format(rec_content,rec_data[1][0],rec_data[1][1]))


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


    udp_socket.bind(("169.254.190.219", 9999))


    while True:
        sendaddr(udp_socket)
        recaddr(udp_socket)
    udp_socket.close()




if __name__ == "__main__":
    main()

四、TCP套接字的创建流程

创建一个客户端的流程

1.创建一个套接字

2.连接服务器

3.发送或者是接收数据

4.关闭套接字

创建tcp客户端套接字的代码:

代码语言:javascript复制
import socket
'''
创建TCP客户端的步骤:
1 创建套接字
2 连接服务端
3 接收或者是发送数据
4 关闭套接字
'''


# def main():
#     tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#     server_addr = ("169.254.190.219",8080)
#
#     # 连接服务器
#     tcp_client.connect_ex(server_addr)
#
#     # 发送数据
#     send_content = "haha"
#     tcp_client.send(send_content.encode("gbk"))
#
#     # 接收数据
#     recv_data = tcp_client.recv(1024)
#     print(recv_data.decode("gbk"))
#     # 关闭套接字
#     tcp_client.close()
#
# if __name__ == "__main__":
#     main()

五、创建服务端的流程

1、创建一个套接字

2.绑定地址和端口

3.将主动改为被动

4.等待客户连接,创建出一个新的套接字

5.关闭套接字

代码如下:

代码语言:javascript复制
'''
创建TCP服务端:
1 创建套接字
2 绑定地址
3 主动变被动
4 等待客户端连接
5 发送或接收数据
6 关闭套接字
'''# def main():
#     # 创建套接字
#     tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#
#     # 绑定地址
#     server_addr = ("169.254.190.219",8080)
#     tcp_server.bind(server_addr)
#
#     # 主动变被动
#     tcp_server.listen(128)
#
#     # 等待客户连接
#     new_client_server, client_addr = tcp_server.accept()
#     print(client_addr)

#
#     # 接收数据
#     recv_data = new_client_server.recv(1024)
#     print(recv_data.decode('gbk'))
#     #
#     # 关闭套接字
#     new_client_server.close()
#     tcp_server.close()
# if __name__ == "__main__":
#     main()

六、如何让一个服务端为多个客户服务

在日程生活中,我们不可能只为单一的客户服务,有可能有多个人同时排队服务。那这种情况下要怎么办呢?

简单的逻辑:

我们要循环主动变被动的过程,每一个客户都会生成一个新的套接字。

代码如下:

代码语言:javascript复制
def main():
    # 创建套接字
    tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


    # 绑定地址和端口
    server_addr = ("169.254.190.219", 8080)
    tcp_server.bind(server_addr)


    # 实现为多个客户端服务
    while True:
        # 变主动为被动
        tcp_server.listen(128)
        new_client_server, client_addr = tcp_server.accept()
        # 为一个客户端多次服务
        while True:
            recv_data = new_client_server.recv(1024)
            if recv_data.decode('gbk'):
                print(recv_data.decode("gbk"))
                new_client_server.send("消息已经收到".encode('gbk'))
            else:
                break
        new_client_server.close()


    tcp_server.close()


if __name__ == "__main__":
    main()

七、使用TCP套接字模拟文件下载器

客户端

如果要使用套接字来模拟文件下载器的话,那么我们客户端大致主要完成的工作:

1.创建套接字

2.连接服务器

3.发送要下载的文件名

4.接收返回的数据

5.保存从服务器接收的数据

6.关闭套接字

实现代码:

代码语言:javascript复制
# @日期:2020-01-10
# @作者:清欢


import socket
'''文件下载器
1.创建一个套接字
2.连接服务器
3.发送要下载的文件名
4.接收服务端返回的数据
5.保存数据
6.关闭套接字
'''
def save_data(circle):
    with open("cilcle_copy.py","w",encoding="utf-8") as f:
        f.write(circle.decode())


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


    # 连接服务器
    server_addr = ("169.254.190.219", 8080)
   tcp_client.connect(server_addr)



    # 发送要下载的数据名
    load_name = input("请输入要下载的文件名")
    tcp_client.send(load_name.encode())


    # 接收返回的数据
    Circle = tcp_client.recv(1024)
    save_data(Circle)
    #


if __name__ == '__main__':
    main()

服务端

如果要完成文件下载器,服务端需要做的工作的大致流程:

1.创建套接字

2.绑定地址和IP

3.化主动为被动,等待客户端连接

4.创建客户的新的套接字

5.接收客户端发过来的文件名

6.读取文件的内容并返回给客户端

7.关闭套接字

代码语言:javascript复制
# 练习
# 清欢


import socket
'''
文件下载器的服务端:
1 创建套接字
2 绑定地址和端口
3 变主动为被动
4 等待接收客户端发过来的文件名
5 读取文件信息
6 将文件信息发送给客户端
7 关闭套接字
'''


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


    # 绑定地址和端口
    server_addr = ("169.254.190.219",8080)
    tcp_server.bind(server_addr)


    # 变主动为被动
    tcp_server.listen(128)


    # 等待客户连接
    new_client_server, client_addr = tcp_server.accept()


    # 接收顾客发过来的文件名
    recv_content = new_client_server.recv(1024).decode()


    # 读取文件内容
    if recv_content:
        with open(recv_content, 'rb') as f:
            file_content = f.read()
            new_client_server.send(file_content)
    else:
        print("输入为空")
    new_client_server.close()
    tcp_server.close()


if __name__ == '__main__':
    main()

TCP与UDP的区别

1、TCP面向连接;UDP是无连接的,即发送数据之前不需要建立连接

2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付

3、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高的通信或广播通信。

4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信

5、TCP对系统资源要求较多,UDP对系统资源要求较少。

0 人点赞