TCP

tcp:tcp使用较多.直接使用较少,使用 封装之后上层的库 较多. 不会有人从头开始写一个tcp的协议,
然后做个什么软件的,造轮子这事情,差不多就得了.知道原理,会使用别人造的库就行.出错了能够找到错误的原因,处理掉就好.

面试常问:tcp和udp的区别

tcp:Transmission Control Protocol 传输控制协议

1.>面向连接(就是打电话的时候先拨号的操作)

2.>可靠的(ACK应答)

3.>基于字节流 stream的方式

面向连接:在通信之前确认双方在线.--> 在通信之前需要先建立连接<拨号>

字节流:无界限,没有量词来界定这个消息的体量,都是连接在一起的.
没有消息边界,对方多次发送的消息,我一次就接收.不是每次都接收一部分.从接收的数据中看不到数据是哪一次发送的. 简称tcp的 "粘包" 问题.

特点:面向连接,可靠,基于字节流

优点:可靠,稳定

缺点:慢,占用系统资源高

步骤:创建连接,数据传送,

不适用广播,一对一的单播

为啥可靠?原因:
1.>发送应答:每发一次都必须得到对方的应答(ACK)

2.>超时重传:一定的时间内没有收到回复,就认为这个数据包已经出错或者丢失,发送方会再次发这个数据包

3.>错误校验:奇偶校验,循环冗余校验等等

4.>流量控制与阻塞管理:发送方有一个动态调整的发送速度的机制.

5.>有序编号

udp中无序,可能重复,有可能乱序.

最大特点有了connect()方法


socket就是一个手机,这么理解即可

1.创建socket(买个手机)
2.建立连接(拨号)
3.发送数据(通话)
4.关闭socket(挂掉)

tcp客户端:

"""
socket就是一个手机,这么理解即可

1.创建socket(买个手机)
2.建立连接(拨号)
3.发送数据(通话)
4.关闭socket(挂掉)

"""

import socket

# 创建
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接
tcp_client_socket.connect(('192.168.14.26', 8089))

while True:

    # 发送数据
    send_data = input('输入发送给服务器的数据:')
    tcp_client_socket.send(send_data.encode())  # 参数是一个字节流bytes类型的数据

    # 接收返回 ,面向连接的(地址肯定是连接的对方的地址)不再需要对方的地址,只需要对方发送的数据
    # 阻塞等待的接收返回
    # recv()返回值的含义:
    # recv()正常情况下是接收到的数据<bytes类型>
    # 如果对方断开连接,返回值就是b''<空字节>,不是对方发送的数据,是系统提示对方断开的标志
    recv_data = tcp_client_socket.recv(1024)  # recv()返回一个数据(bytes类型)即可,没有地址信息
    # print(recv_data.decode('gbk'))  # 解码解得是调试助手发来的数据,而调试助手发送的时候是gbk的编码格式
    # 断开连接输出:b''  <空字节>
    # print(recv_data)  # 解码解得是调试助手发来的数据,而调试助手发送的时候是gbk的编码格式
    # recv_data != None and recv_data !=''
    if recv_data:  # 如果recv_data不为空,为真
        print(recv_data.decode('gbk'))
    else:  # 为空,终止
        print('服务器断开连接')
        break
# 关闭
tcp_client_socket.close()

tcp服务器:

"""
tcp服务器:
1.>socket()买个手机

2.>bind()绑定10086

3.>listen()安装客户服务系统,设定等待服务区的大小,从等待区域中取出一个客户(和客户建立连接)

4.>accept()并转接到分机,具体的同事使用分机和客户交流

5.>close()分机挂机

总计挂机

总机不会挂机
分机会挂机
实际在交流信息的时候是和分机交流
总机只是和客户建立连接,分到等待区

服务器分为两种socket,一种专门来接受客户的连接请求,一种才是和客户进行数据交流

服务器是被动的
"""
import socket

# 创建服务器socket
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定,如果不绑定,别人就找不到这个地址了
tcp_server_socket.bind(('', 9999))  # ip为空,意味着绑定了这台主机上面的所有网卡,一台机子上面不是只有一个网卡,至少是2个网卡

# 监听等待  安装客户服务系统 --->1.将socket设置为被动socket<被动接受连接  默认是主动连接>  2.设置等待服务区的大小为128
tcp_server_socket.listen(128)
print('服务器已建立连接')
# 转到分机接收     从等待服务区取出一个客户端来服务
# 返回值是一个元组,两个元素,第一个元素是客户端socket<分机>,第二个是客户端的(ip,port)<地址>
tcp_client_socket, client_info = tcp_server_socket.accept()  # 无参数,只有返回值,返回值是socket和 address_info
print('接受到客户%s的连接请求' % str(client_info))

while True:
    # 使用分机和客户端进行交流    ----> 数据收发
    recv_data = tcp_client_socket.recv(1024)
    if recv_data:
        print('接收到的客户数据:', recv_data.decode())
    else:  # b''
        print('客户端下线了')
        break

    # 服务器返回给客户信息
    tcp_client_socket.send('are you OK?'.encode())

# 分机关闭
tcp_client_socket.close()
# 总机关闭
tcp_server_socket.close()

# 只有客户端才需要知道服务器的ip和端口信息,服务器不需要知道客户的信息,再建立了连接之后会收到的
"""<对象>"""

"""一定是先开服务器,后开客户端"""

tcp服务器升级版:

"""
tcp服务器:
1.>socket()买个手机

2.>bind()绑定10086

3.>listen()安装客户服务系统,设定等待服务区的大小,从等待区域中取出一个客户(和客户建立连接)

4.>accept()并转接到分机,具体的同事使用分机和客户交流

5.>close()分机挂机

总计挂机

总机不会挂机
分机会挂机
实际在交流信息的时候是和分机交流
总机只是和客户建立连接,分到等待区

服务器分为两种socket,一种专门来接受客户的连接请求,一种才是和客户进行数据交流

服务器是被动的
"""
import socket

# 创建服务器socket
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定,如果不绑定,别人就找不到这个地址了
tcp_server_socket.bind(('', 9999))  # ip为空,意味着绑定了这台主机上面的所有网卡,一台机子上面不是只有一个网卡,至少是2个网卡

# 监听等待  安装客户服务系统 --->1.将socket设置为被动socket<被动接受连接  默认是主动连接>  2.设置等待服务区的大小为128
tcp_server_socket.listen(128)
print('服务器已建立连接')

# 就是领导接活,下面的小弟只管干活
while True:
    # 转到分机接收     从等待服务区取出一个客户端来服务
    # 返回值是一个元组,两个元素,第一个元素是客户端socket<分机>,第二个是客户端的(ip,port)<地址>
    tcp_client_socket, client_info = tcp_server_socket.accept()  # 无参数,只有返回值,返回值是socket和 address_info
    print('接受到客户%s的连接请求' % str(client_info))

    while True:
        # 使用分机和客户端进行交流    ----> 数据收发
        recv_data = tcp_client_socket.recv(1024)

        # 服务器返回给客户信息,  接收到的是gbk的编码数据流,需要解码后再次编码为utf-8
        tcp_client_socket.send(recv_data.decode('gbk').encode())
        if recv_data:
            print('接收到的客户数据:', recv_data.decode('gbk'))
        else:  # b''
            print('客户端下线了')
            break

    # 分机关闭
    tcp_client_socket.close()
    # # 总机关闭
    # tcp_server_socket.close()

    # 我需要被别人找到,就要使用bind()

    # 想要接受连接,必须先listen()
    # 收发数据都是client_socket
    # accept() 接受:被动的
    # close() 断开连接请求
    # 0字节长度 ---> 对方断开连接了
    # 解阻塞:由卡死到不卡的状态变化.

文件下载器-客户端:

"""
客户端:  1.连接服务器(ip,port)
        2.下载文件的名字
服务器接收到客户端需要下载的文件名字,搜索文件,传给客户端
客户端写入到自己的文件中.


"""
# 1. 输入服务器ip,端口
# 2. 建立socket 连接服务器
# 3. 输入文件名
# 4. 发送文件名给服务器
# 5. 使用socket接收来自服务器的数据(保存在文件中)
# 6. 如果收到b'',说明服务器断开连接-已经下载好
# 7. 关闭socket


import socket

server_ip = input('输入server的ip:')
server_port = int(input('输入server的port:'))

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

client_socket.connect((server_ip, server_port))

file_name = input('输入下载的文件名:')

client_socket.send(file_name.encode())

# 文本方式打开   str类型  需要解码  图片无法解码,所以直接使用二进制方式打开即可
with open("download" + file_name, 'wb') as file:
    while True:
        file_data = client_socket.recv(4096)  # ConnectionAbortedError: [WinError 10053] 你的主机中的软件中止了一个已建立的连接。原因是服务器出错了
        # 如果对方断开连接,我们就接收到b''
        if not file_data:
            print('服务器断开连接,说明文件传输完成')
            break

        # 否则就将收到的数据直接写入带文件中
        file.write(file_data)

client_socket.close()

文件下载器-服务器:

"""
服务器:
# 1. 创建服务器socket
# 2. bind() listen
# 3. 接受客户端连接请求,取出一个客户端关联的socket
# 4. 使用关联的socket和客户端进行通信
#    收客户端需要下载的文件名,发送文件名对应的数据
# 5. 发送完成,断开连接<就是关闭了client_socket
# 关闭服务器socket


"""


import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置选项  SO_REUSEADDR:二次利用,释放地址,重新利用地址
# 忽略2msl等待时间,直接进行这个地址的再次利用
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('', 9991))
server_socket.listen(128)

while True:
    client_socket, client_info = server_socket.accept()
    # print('接收到连接请求%s' % client_info)  #  TypeError: not all arguments converted during string formatting
    # print('接收到%s连接请求' % str(client_info))  # 记得这里的client_info是一个元组类型,不能使用%号来连接,客户端那边也会报错
    print('接收到连接请求:', client_info)  # 记得这里的client_info是一个元组类型,不能使用%号来连接,客户端那边也会报错

    file_name = client_socket.recv(256).decode()  # 文件名一般很短,256字节长度可以了

    # 打开本地文件,读取数据
    try:
        # 捕获如果没有这个文件的异常
        file = open(file_name, 'rb')
    except Exception as e:
        pass
    else:
        # 读取
        file_data = file.read()
        # 发送
        client_socket.send(file_data)
        # 关闭文件
        file.close()
    finally:
        # 关闭socket
        client_socket.close()

server_socket.close()
原文地址:https://www.cnblogs.com/huaibin/p/12097758.html