UDP聊天器和TCP的收发数据

时间:2022-07-01 10:17:51

  • 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kun1280437633/article/details/80316396

1. 不同电脑上的进程之间如何通信

首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!

在1台电脑上可以通过进程号(PID)来唯一标识一个进程,但是在网络中这是行不通的。

其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用进程(进程)。

这样利用ip地址,协议,端口就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互

注意:
  • 所谓进程指的是:运行的程序以及运行时用到的资源这个整体称之为进程(在讲解多任务编程时进行详细讲解)
  • 所谓进程间通信指的是:运行的程序之间的数据共享
  • 后面课程中会详细说到,像网络层等知识,不要着急

2. 什么是socket

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

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

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

3. 创建socket

在 Python 中 使用socket 模块的函数 socket 就可以完成:

import socket
socket.socket(AddressFamily, Type)

说明:

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

  • Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
  • Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
说明
  • 套接字使用流程 与 文件的使用流程很类似
    1. 创建套接字
    2. 使用套接字收/发数据
    3. 关闭套接字
    4. 如果需要做成一个服务器端的程序的话,是需要绑定端口的
str->bytes:encode编码
bytes->str:decode解码

4. 什么是端口号

端口号是唯一标识运行网络进程的数字

(1)端口号的作用就是区分同一台电脑上不同的进程

(2)端口号在通过一台电脑上不能有重复使用

(3)端口号在不同电脑上可以重复

(4)端口有两种分配方式:固定端口(绑定),随机端口(系统分配)

(5)端口号有:知名端口(0-1023)、动态端口(1024-65535)

4. UDP和TCP区别

UDP聊天器和TCP的收发数据

代码:

UDP聊天器

import socket

def send_msg(udp_socket):
    """发送消息"""
    send_msg = input('请输入要发送的数据:')
    send_ip = input('请输入接收消息的IP地址:')
    send_port = int(input('请输入要接收消息的端口号:'))
    send_addr = (send_ip, send_port)
    udp_socket.sendto(send_msg.encode('utf-8'), send_addr)

def recv_msg(udp_socket):
    """接收消息"""
    print('准备接收数据:')
    recv_data, recv_addr = udp_socket.recvfrom(1024)
    print('%s 发来消息: %s' % (recv_addr, recv_data.decode('utf-8')))  # Ubuntu 网络调试助手发来的消息使用 UTF-8解码

def main():
    """使用一个socket实现收和发的功能"""
    # 创建UDP套接字--怎么在代码提示的时候不需要区分大小写?
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    local_addr = ("", 1314) #下标0是本机IP地址,下表1是使用的端口号
    udp_socket.bind(local_addr)

    while True:
        # 打印提示信息
        print('==============================')
        print('1:发送消息')
        print('2:接收消息')
        print('==============================')

        # 获取用户选择的操作
        command = input('请输入要操作的功能序号:')

        # 判断用户的操作类型
        if command == '1':
            # 发送数据
            send_msg(udp_socket)

        elif command == '2':
            # 接收数据
            recv_msg(udp_socket)

        else:
            # 无法处理的指令,退出循环
            break

    # 关闭套接字
    udp_socket.close()

if __name__ == '__main__':

    main()

import socket
import threading

UDP聊天器多任务版

def send_msg(udp_socket):
    """发送消息"""
    while True:
        send_msg = input('请输入要发送的数据:')
        send_ip = input('请输入接收消息的IP地址:')
        send_port = int(input('请输入要接收消息的端口号:'))
        send_addr = (send_ip, send_port)
        udp_socket.sendto(send_msg.encode('utf-8'), send_addr)

def recv_msg(udp_socket):
    """接收消息"""
    while True:
        print('准备接收数据:')
        recv_data, recv_addr = udp_socket.recvfrom(1024)
        print('%s 发来消息: %s' % (recv_addr, recv_data.decode('utf-8')))  # Ubuntu 网络调试助手发来的消息使用 UTF-8解码

def main():
    """使用一个socket实现收和发的功能"""
    # 创建UDP套接字--怎么在代码提示的时候不需要区分大小写?
    udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    local_addr = ("", 1314)
    udp_socket.bind(local_addr)

    # 开启发送线程
    # send_msg(udp_socket)
    t1 = threading.Thread(target=send_msg, args=(udp_socket,))
    t1.start()

    # 开启接收线程
    # recv_msg(udp_socket)
    t2 = threading.Thread(target=recv_msg, args=(udp_socket,))
    t2.start()

    # 关闭套接字
    # udp_socket.close()

if __name__ == '__main__':
    main()

TCP客户端的收发数据

import socket

def main():
    """使用TCP协议发送和接收数据"""
    # 创建TCP套接字, 参数二是 SOCK_STREAM
    tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # TCP客户端使用时必须先连接到服务器
    server_addr = ('127.0.0.1', 8080)
    tcp_client.connect(server_addr)  # connect 接收一个元组

    # 发送数据
    tcp_client.send(b'hahaha')  # 只要发送数据,不需要接收者的地址

    # 接收数据
    print('准备接收数据:')

    recv_data = tcp_client.recv(1024)  # 接收到的只有数据,没有发送者的地址
    print(recv_data.decode('utf-8'))

    # 关闭套接字
    tcp_client.close()

if __name__ == '__main__':

    main()

TCP服务器的收发数据

import socket

def main():
    """使用TCP服务器完成一个客户端的数据收发"""
    # 创建TCP套接字
    tcp_sever = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 绑定地址信息
    local_addr = ("", 1315)
    tcp_sever.bind(local_addr)

    # 设置为被动模式--128是最大可以同时连接的客户端数量
    tcp_sever.listen(128)

    while True:
        # 获取客户端连接,得到一个元组,0号下标是和客户端连接的套接字,1号下标是客户端地址
        print('等待客户端连接----')
        client_soc, client_addr = tcp_sever.accept()

        while True:
            # 向客户端发送数据
            print('发送数据~~')
            client_soc.send(b'hahaha')

            # 接收客户端数据
            print('准备接收数据!')
            recv_msg = client_soc.recv(1024)

            # 判断是否要断开客户端链接
            if recv_msg:
                # 有数据
                print("接收到数据:",recv_msg)

            else:
                # 没有收到数据
                print('客户端已经断开链接')
                break

        # 关闭客户端套接字
        client_soc.close()

    # 关闭服务器套接字
    tcp_sever.close()

if __name__ == '__main__':
    main()