Python高级 网络tcp

Python高级 网络tcp

一. TCP介绍

1. tcp协议 
	TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的通信协议
	 1. tcp是面向连接的,可靠的,基于字节流的通信协议,好比打电话模型,先连接,再通信
	 2. udp是面向无连接的,不可靠,基于数据报的通信协议, 好比发短信模型,只管发
	     数据报方式: 独立的数据单元,包含目标主机的ip和端口和要发送的内容






ps: 协议是什么

通信双方的一种约定,一种规则,通信双方都认同和遵守,且按照这种约定或规则,能实现双方更好的,有条不紊的通信

二、采用tcp协议实现网络通信(重点)

tcp通信方式 是严格的服务端与客户端之分,而udp没有
1. TCP客户端实现
  编写步骤:
    1. 创建tcp 的socket对象
    2. 与服务端建立连接
    3. 通信(发送与接收数据)
    4. 关闭socket对象,释放连接
"""TCP客户端实现
编写步骤:
1. 创建tcp 的socket对象
2. 与服务端建立连接
3. 通信(发送与接收数据)
4. 关闭socket对象,释放连接
"""
import socket




def main():

    # 1. 创建tcp 的socket对象

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



    # 2. 与服务端建立连接
    # the address is a pair (host, port)
    # server_addr = ('192.168.110.66',8080) # 是元组类型, 第一个元素: ip(字符串) ,第二个 端口号(整数)
    server_addr = ('192.168.110.71', 9999)  # 是元组类型, 第一个元素: ip(字符串) ,第二个 端口号(整数)
    tcp_client_socket.connect(server_addr)

    # 3. 通信(发送与接收数据)
    # 3.1 发送数据到服务端
    # 准备要发送的数据
    send_data = input("请输入要发送的数据:")

    # 字符串转为二进制串
    data = send_data.encode('utf-8')
    tcp_client_socket.send(data)

    # 3.2 等待接收服务端回传的消息, 阻塞状态
    # recv_data=tcp_client_socket.recvfrom(1024) # 一次最多接收1024字节
    recv_data = tcp_client_socket.recv(1024)  # 一次最多接收1024字节

    # 显示接收到的数据
    # 通过socket.recvfrom取得的数据 (b'www', None)
    # 通过socket.recv取得的数据 : b'uuu'
    # 解码, 把二进制串转为字符串
    # data=recv_data.decode('gbk')
    data = recv_data.decode('utf-8')
    print("接收到的数据:", data)

    # 4.关闭socket对象,释放连接
    tcp_client_socket.close()




if name == 'main':

    main()  # alt+enter

  

2. tcp服务端实现
   编写步骤:
    1. 创建服务端的socket,该socket用于监听客户端的请求
    2. 绑定端口
    3. 开启监听
    4. 等待接受客户端的连接请求,一有请求,则立即接受,且创建一个新的socket,该socket是用于与客户端通信的
    5. 通过新创建的socket与客户端通信
    6. 关闭新创建的与客户端通信的socket, 该socket关闭,则与客户端通信结束,即不再为该客户端服务
    7. 关闭用于监听的socket,该socket关闭,则不再接收新的客户端请求
"""tcp服务端实现
   编写步骤:
    1. 创建服务端的socket,该socket用于监听客户端的请求
    2. 绑定端口
    3. 开启监听
    4. 等待接受客户端的连接请求,一有请求,则立即接受,且创建一个新的socket,该socket是用于与客户端通信的
    5. 通过新创建的socket与客户端通信
    6. 关闭新创建的与客户端通信的socket, 该socket关闭,则与客户端通信结束,即不再为该客户端服务
    7. 关闭用于监听的socket,该socket关闭,则不再接收新的客户端请求
"""
import socket




def main():

    # 1. 创建服务端的socket,该socket用于监听客户端的请求

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



    # 2. 绑定端口
    server_socket.bind(('', 9999))

    # 3. 开启监听
    server_socket.listen(5)
    print("静静等待客户端的请求")

    # 4.等待接受客户端的连接请求,一有请求,则立即接受,且创建一个新的socket,该socket是用于与客户端通信的
    # accept:  阻塞状态
    # new_sock: 新创建的socket,是与客户端通信的socket
    # client_addr: 来自客户端的地址
    new_sock, client_addr = server_socket.accept()
    print("有新的客户端请求,来自于-->", client_addr)

    # 5. 通信新创建的socket与客户端通信
    # 5.1: 等待接收客户端的请求消息
    recv_data = new_sock.recv(1024)
    # 解码
    data = recv_data.decode('gbk')
    print("接收到的客户端的数据:", data)

    # 5.2 回复消息给客户端,发送消息给客户端
    new_sock.send('欢迎光临'.encode('utf-8'))

    # 6.关闭新创建的与客户端通信的socket, 该socket关闭,则与客户端通信结束
    new_sock.close()

    # 7.关闭用于监听的socket,该socket关闭,则不再接收新的客户端请求
    server_socket.close()




if name == 'main':

    main()
  1. tcp客户端与服务端交互过程
    (结合图理解)

三、tcp与udp深入认识

1. tcp通信的三个步骤
   - 创建连接
   - 传输数据
   - 关闭连接
  
2. TCP特点
	a>面向连接
	  - 1. tcp是面向连接的, 先连接,再通信
	  - 2. tcp的连接是一对一的
	       udp可以一对一,可以一对多 ,udp适合做广播相关的程序
	  
	b>可靠传输 ,tcp比udp更可靠
	  - 发送应答机制
	  - 超时重传
	  - 错误校验
	  - 流量控制| 阻塞管理

3. TCP与UDP的不同点
	面向连接
	有序数据传输
	重发丢失的数据包
	舍弃重复的数据包
	无差错的数据传输
	阻塞/流量控制

4. tcp与upd应用场景:  
   1. udp: 面向无连接的,可一对一或一对多,相对tcp速度更快,实时性更好,耗资源更少,但稳定性、可靠性比tcp差 
   		- 多点通信,比如广播通信 
   	    - 当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,更实时,使用UDP ,比如QQ语音,QQ视频
   2. tcp: 面向连接,且一对一,比udp更稳定,可靠,适合通信质量要求较高的场景
      http传输,文件传输,smtp等等,目前大部分的传输都会基于tcp协议传输

四、TCP网络编程加强

 1. 同时为多个客户端提供服务

 	    while True:
	        # 等待接收客户度的请求,一有请求,则立即响应,并且创建一个与客户端对接的socket,实现与客户端通信
	        new_sock, client_addr = server_socket.accept()


 2. 为同一个客户端提供多次服务
 		 while True:
	        # 5. 通过新创建的socket与客户端的socket实现通信
	        recv_data = new_sock.recv(1024)
	        print("收到的消息:",recv_data)
	        new_sock.send('欢迎光临!'.encode('utf-8'))

 3. 如何判断客户端已经下线
 	当客户端的套接字调用close后,服务端会recv解堵塞,并且返回的长度为0(一个空的消息), 因此服务端可以通过返回数据的长度来区别客户端是否已经下线

 		    # 5.1 接收客户端发送的消息
		    recv_data = new_sock.recv(1024)
		    if recv_data:
		        print("接收到的客户端消息:", recv_data.decode('gbk'))
		        # 5.2 发送消息给客户端
		        new_sock.send('欢迎光临!'.encode('utf-8'))
		    else:
		        print("客户端已经下线...")
		        break # 退出内部的循环,跳到外部的循环

 4. listen中数值的含义
 	server_socket.listen(5)  # backlog: 积压未办之事, 好比设置排队队列中的最大排队人数

 5. 服务器如何立即复用同一个端口而不会提示端口被占用

 	"""
 	    server_socket.setsockopt(level,option,value) :配置socket 
        level:等级,对哪个等级操作(ip,tcp,udp,socket等级)
        option:设置哪个选项
        		socket.SO_REUSEADDR: 复用地址 
        value: True:表示复用,False,表示不复用
    """
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    # 提示:socket.setsockopt()方法要在 socket.bind()之前设置

五、三次握手、四次挥手(了解)

	a> 三次握手

		第一次握手: 客户端A向服务端B发送连接请求(客户端-->服务端方向)

		第二次握手: 服务端B向客户端A发送确认连接,同时向客户端A发送连接请求(服务端-->客户端方向) 

		第三次握手: 客户端A收到服务端的确认信息,正确无误后,再向服务端发送确认连接信息



	b> 四次挥手

六、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,因此服务端可以通过返回数据的长度来区别客户端是否已经下线

七、综合案例:文件下载器

a>客户端实现
	"""文件下载器客户端实现流程
		1. 创建一个tcp socket
		2. 连接服务端
		3. 获取用户输入的文件名
		4. 把要下载的文件名发给服务端
		5. 等待接收服务端回传的文件内容
		6. 假如内容不为空,则写入到文件中
		7. 关闭tcp socket		
	"""
"""tcp客户端实现
文件下载器客户端实现流程
    1. 创建一个tcp socket
    2. 连接服务端
    3. 获取用户输入的文件名
    4. 把要下载的文件名发给服务端
    5. 等待接收服务端回传的文件内容
    6. 假如内容不为空,则写入到文件中
    7. 关闭tcp socket
"""
import socket




def main():

    # 1.1. 创建一个tcp socket

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



    # 2. 连接服务端
    # tcp_client_socket.connect(('192.168.110.66', 8080))
    tcp_client_socket.connect(('192.168.110.71', 9999))

    # 3. 获取用户输入的文件名
    file_name = input("请输入要下载的文件名:")

    # 4.把要下载的文件名发给服务端
    tcp_client_socket.send(file_name.encode())  # 不写编码,linux默认是utf-8编码

    # 5.等待接收服务端回传的文件内容
    recv_data = tcp_client_socket.recv(1024*1024)

    print(len(recv_data))  # 测试接收到的数据的长度
    # 6. 假如内容不为空,则写入到文件中
    if recv_data:
        file = open("/home/python/Desktop/" + file_name, 'wb')
        file.write(recv_data)
        file.close()

    # 7.关闭tcp socket
    tcp_client_socket.close()




if name == 'main':

    main()

	

b>服务端实现
	""" 文件下载服务端实现流程
		1. 创建服务端的socket,用来做监听的
		2. 绑定端口
		3. 开启监听 listen
		4. 循环接收客户端的下载请求
		5. 获取客户端发送过来的要下载的文件名
		6. 通过函数取得要下载的文件的内容
		7. 把内容回传给客户端
		8. 关闭与客户端对接的socket
		9. 关闭服务端server_socket,不再接收新的下载文件的请求
	"""
""" 文件下载服务端实现流程
    1. 创建服务端的socket,用来做监听的
    2. 绑定端口
    3. 开启监听 listen
    4. 循环接收客户端的下载请求
    5. 获取客户端发送过来的要下载的文件名
    6. 通过函数取得要下载的文件的内容
    7. 把内容回传给客户端
    8. 关闭与客户端对接的socket
    9. 关闭服务端server_socket,不再接收新的下载文件的请求
"""
import socket




def get_file_content(file_name):

    """

    通过文件名取得文件内容

    :param file_name: 文件名

    :return: 文件的内容

    """

    try:

        file = open(file_name, 'rb')

        content = file.read()

        file.close()

        return content

    except:

        print("%s文件名找不到" % file_name)



def main():

    # 1. 创建服务端的socket,用来做监听的

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



    # 复用端口
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

    # 2. 绑定端口
    server_socket.bind(('', 9999))

    # 3. 开启监听 listen
    server_socket.listen(6)
    print("等待客户端的请求:")

    # 4. 循环接收客户端的下载请求
    while True:
        new_sock, client_addr = server_socket.accept()

        # 5. 获取客户端发送过来的要下载的文件名
        recv_data = new_sock.recv(1024)
        # 解码
        file_name = recv_data.decode('utf-8')
        print("客户端要下载的文件名:%s,来自-->%s" % (file_name, str(client_addr)))

        # 6.通过函数取得要下载的文件的内容
        file_content = get_file_content(file_name)

        # 7. 把内容回传给客户端
        if file_content:
            new_sock.send(file_content)  # file_content已经是二进制串了

        # 8. 关闭与客户端对接的socket
        new_sock.close()

    # 9. 关闭服务端server_socket,不再接收新的下载文件的请求
    server_socket.close()




if name == 'main':

    main()
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值