TCP是面向连接的网络通信方式,数据传输之前需要建立一条客户端和服务端之间的专有连接,所以它是严格区分客户端和服务端的,它的特点是稳定、可靠,且有流量控制和拥塞控制,是目前最常用的通信方式。

TCP的通信步骤

  • 建立连接
  • 数据传送
  • 终止连接

TCP的可靠传输机制

1.发送应答

TCP发送的每个报文段都必须得到接收方的应答才能认为这个TCP报文段传输成功。

2.超时重传

发送端发出一个报文段之后就启动定时器,如果超过时间还没有接收到应答就重新发送这个报文段。TCP为了保证不丢包,就给每个包一个序号,接收端每收到一个数据包就发送一个确认(ACK)下载东西时,上传的数据就是应答数据包。

3.错误校验

TCP用一个校验和函数来检验数据是否有错误,在发送和接收时都要计算校验和。

4.流量控制和阻塞管理

流量控制用来避免发送方发送数据太快而接收方来不及收下的问题。

创建TCP客户端

# -*- coding: utf-8 -*-
import socket

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

    # 与服务器建立连接
    s.connect(("192.168.32.128", 60000))

    # 发送数据
    message = input("请输入要发送的数据:")
    s.send(message.encode("gbk"))
    s.close()

if __name__ == '__main__':
    main()

创建TCP服务端

首先看一下流程 - socket创建一个套接字 - bind绑定ip和port - listen使套接字由主动变为被动连接,这样才能让别人去连接你 - accept等待客户端连接 - recv/send接收发送数据

# -*- coding: utf-8 -*-
import socket


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

    # 绑定套接字
    s.bind(("192.168.32.1", 8838))

    # 监听端口,监听时阻塞,知道有一个客户端连接
    # 128表示同时可接受的连接数量
    s.listen(128)

    while True:
        # 接收连接,生成的是一个新的服务套接字,该套接字专为此连接服务,另一个是该客户端的地址
        # 等待连接之前是阻塞的
        print("等待客户端连接中.....")
        client_socket, clientAddr = s.accept()
        print("客户端已连接:"+ str(clientAddr[0]))

        # 接收信息,接收之前是阻塞状态
        recv_data = client_socket.recv(1024)
        print("收到的数据是:"+ str(recv_data))

        # 返回信息
        message = input("请输入您要发送的数据:")
        client_socket.send(message.encode("gbk"))

        client_socket.close()
        print("此客户端已关闭.....")
    s.close()


if __name__ == '__main__':
    main()

同一时间只会为一个客户端服务,其他的客户端可以连接成功,但是要排队。等待上一个客户端连接完成才能进行数据的收发。

但是上面的代码意味着只能为一个连接服务一次,如果一个客户端要对服务器进行多次请求。就满足不了了,因此需要进行改进!

# -*- coding: utf-8 -*-
import socket


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

    # 绑定套接字
    s.bind(("192.168.32.1", 8838))

    # 监听端口,监听时阻塞,知道有一个客户端连接
    s.listen(128)

    # 循环为多个客户端服务
    while True:
        # 接收连接,生成的是一个新的服务套接字,该套接字专为此连接服务,另一个是该客户端的地址
        # 等待连接之前是阻塞的
        print("等待客户端连接中.....")
        client_socket, clientAddr = s.accept()
        print("客户端已连接:"+ str(clientAddr[0]))

        # 循环为一个客户服务
        while True:
            # 接收信息,接收之前是阻塞状态
            recv_data = client_socket.recv(1024)
            print("收到的数据是:" + str(recv_data))

            # 如果recv解阻塞,那么有两种方式
            # 1.客户端发来数据
            # 2.客户端断开连接
            if recv_data:
                # message = input("请输入您要发送的数据:")
                # message = input("请输入您要发送的数据:")
                client_socket.send("okokokokokokok".encode("gbk"))
            else:
                break

        client_socket.close()
        print("此客户端已关闭.....")

        command = input("是否退出?n/y:")
        if command == "y":
            break
        elif command == "n":
            continue
        else:
            print("命令输入有误,请重新输入")
    s.close()


if __name__ == '__main__':
    main()

如果recv的返回值为空,那么客户端已经断开了连接。

但是目前还不能同时为多个客户端服务,以后讲多线程的时候能做到。