三次握手和四次挥手_三次握手与四次挥手

28e2ba01a58d05c0807fbb7cca0080f4.png
封面图片来源:沙沙野

内容概览

  1. 三次握手与四次挥手
  2. TCP 协议与 UDP 协议的流程图
  3. socket 层
  4. socket 基本运用
  5. 传输中文时的方法
  6. 带退出的聊天程序
  7. 小练习
  8. 服务端同时接收多个客户端消息

三次握手与四次挥手

  1. TCP 协议之所以传输稳定、可靠,就是因为它有三次握手和四次挥手的过程
  2. 先建立连接,进行三次握手:
  • 就好比 QQ 客户端 给服务器发送一个值 1200,问它,"能收到我的消息吗"
  • 这时服务器回答,"可以",并且返回给 QQ 客户端一个值 1201 来表示接收到了。然后服务器要问 QQ 客户端,"能收到我的消息吗"
  • QQ 客户端回复,"可以"
  • 这就是建立连接的三次握手。注意,服务器发送 1201 和问 QQ 客户端归于一次握手

3. 数据传输的过程

  • QQ 客户端给服务器发送一条消息
  • 服务器收到后给 QQ 客户端返回一条消息
  • 这两个过程合在一起才算一个完整的数据传输

4. 断线连接四次挥手

  • QQ 客户端给服务器发送一条消息,"我要结束通话了"
  • 服务器接收到后,回复一条消息,"收到了"
  • 服务器又回复,"我也结束通话了"
  • QQ 客户端回复,"好的"

TCP 协议与 UDP 协议的流程图

9eb4125a01cecda6150e592c102e091f.png

socket 层

  1. 所处位置:在应用层和下面所有层之间的位置
  2. 有了 socket 可以以相对简单的方式进行网络通信
  3. 本质上是帮助我们解决两个程序之间通信
  4. socket 是应用层与 TCP/IP 协议通信的中间软件抽象层,是一组接口

60dce6f8fa18d485ee22b68481c95db3.png

socket 基本运用

  1. 下面开始举例加深对 socket 的理解。首先,新建一个文件,命名为 "server.py",在同一目录下再建一个文件,命名为 "client.py"
  2. TCP 是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
  3. server.py 中的内容
# 导入套接字的包
import socket

sk = socket.socket()
# 把地址绑定到套接字
sk.bind(("127.0.0.1", 8080))
# 注意:元组的第一个元素是字符串形式的 IP 地址,必须是本电脑的
# IP 地址有两种
#   a. 192.168.13.4, 在cmd里面输入 ipconfig 即可查询
#   b. 127.0.0.1, 本地回环地址

# 这两个ip地址的作用是不一样的
#   a. 192.168.... 表示所有的和我在同一个局域网的小伙伴都能访问
#   b. 127.0.0.1   则只有在自己电脑的 client 才能用

# 元组内的第二个元素是端口号,一般在8000-10000之间


# 三次握手
# 监听链接
sk.listen()
# 阻塞,直到有一个客户端来连接
conn, addr = sk.accept()  
print(addr)

# 注意发送的内容必须是 bytes 类型
conn.send(b"Hi, can you receive my message?")
# 最多接收1024个字节,没1024时有多少接收多少
msg = conn.recv(1024)
print(msg)

# 四次挥手
# 关闭客户端套接字
conn.close()
# 关闭服务器套接字
sk.close()

4. client.py 中的内容

import socket

# 创建客户端套接字
sk = socket.socket()
# 把三次握手建立起来了,尝试连接服务器
# 这里 connect 一次,上面(server.py)就 accept 一次
sk.connect(("127.0.0.1", 8080))

# 对话(发送/接收)
msg = sk.recv(1024)
print(msg)

sk.send(b"Yep, How are you?")
# 关闭客户端套接字
sk.close()

# 然后先运行 server.py, 再运行 client.py,两个都会有结果
# 注意收发次数要相等,还要一一对应

5. 先运行 server.py,再运行 client.py,两个都会有结果

# server.py 运行后,会先把消息发给客户端,并处于等待状态
# 待客户端接收到并反馈后,才有以下结果:
('127.0.0.1', 42060)
b'Yep, How are you?'

# client.py 运行后,马上接收到了服务端发来的消息,然后反馈回去
# 运行结果:
b'Hi, can you receive my message?'

传输中文时的方法

  1. 无论是服务端还是客户端,发送中文信息前先对消息进行编码;接收中文信息后,对消息进行解码
  2. server.py 中的内容示例
import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 8080))

sk.listen()
conn, addr = sk.accept()

# 发送消息前,对其编码
conn.send("你好,能接收到我的消息吗?".encode())

recv_msg = conn.recv(1024)
# 接收消息后,对其解码
decode_recv_msg = recv_msg.decode("utf-8")
print(decode_recv_msg)

conn.close()
sk.close()

3. client.py 中的内容示例

import socket

sk = socket.socket()
sk.connect(("127.0.0.1", 8080))

msg = sk.recv(1024)
# 接收消息后,对其解码
decode_msg = msg.decode()
print(decode_msg)

# 发送消息前,对其编码
sk.send("你好,可以接收到你发的消息".encode())

sk.close()

4. 先运行 server.py,再运行 client.py,运行结果如下

# server.py
你好,可以接收到你发的消息

# client.py
你好,能接收到我的消息吗?

带退出的聊天程序

  1. 上面的示例中服务端与客户端只是建立一次链接,实际运用中肯定不是连一次对话一次,因此,可以写一个程序,能够让双方都保持通信状态,并且可以随时结束聊天
  2. server.py 中的内容
import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 8800))

sk.listen()
conn, addr = sk.accept()
# 注意在 accept 之后加上循环
# 是为了能够让我们和一个客户端多说几句话
while 1:
    send_msg = input("msg: ")     
    conn.send(send_msg.encode())
    # 设置结束聊天的条件
    if send_msg == "q": break

    msg = conn.recv(1024).decode()
    if msg == "q": break
    print(msg)

conn.close()
sk.close()

3. client.py 中的内容

import socket

sk = socket.socket()
sk.connect(("127.0.0.1", 8800))
while 1:
    msg = sk.recv(1024).decode()
    if msg == "q": break
    print(msg)

    send_msg = input("msg: ")
    sk.send(send_msg.encode())
    if msg == "q": break

sk.close()

4. 先运行 server.py,再运行 client.py,双方可以一直保持通信状态,当有一方输入 q 之后,相互之间的通信结束

# 注意:如果运行 server.py 时报错:
Traceback (most recent call last):
  File "server.py", line 4, in <module>
    sk.bind(("127.0.0.1", 8800))
OSError: [Errno 98] Address already in use

# 解决办法:把两个文件中的端口号改一下即可,比如把 8800 改为 8000 再依次重新运行


# 带有 msg: 的表示发送的消息,不带的就是接收到的消息
# server.py
msg: 你好  
下午好
msg: 很高兴和你聊天
我也是
msg: 再见
再见
msg:  q

# client.py
你好
msg: 下午好
很高兴和你聊天
msg: 我也是  
再见
msg: 再见

小练习

  1. 问题描述:所有的 client 端都要以 server 端的时间为基准,client 端发送一个时间格式——"%Y-%m-%d %H:%M:%S",server 端根据接收到的时间格式向客户端返回时间
  2. 大概思路解析:按照上面的题意,应该是先客户端向服务端发送消息,服务端接收到后再进行处理,而不是像上面的示例一样,先服务端发送消息
  3. server.py 中的内容
import socket
import time

sk = socket.socket()
sk.bind(("127.0.0.1", 9000))
sk.listen()

conn, addr = sk.accept()

msg = conn.recv(1024)
print("接收到客户端传来的消息:%s" % msg)

format_msg = time.strftime(msg.decode())
conn.send(format_msg.encode())

conn.close()
sk.close()

4. client.py 中的内容

import socket

sk = socket.socket()
sk.connect(("127.0.0.1", 9000))

sk.send(b"%Y-%m-%d %H:%M:%S")

msg = sk.recv(1024).decode()
print("接收到服务端传来的消息:%s" % msg)

sk.close()

5. 同样先运行 server.py,再运行 client.py

# server.py
接收到客户端传来的消息:b'%Y-%m-%d %H:%M:%S'

# client.py
接收到服务端传来的消息:2019-11-25 17:37:52

服务端同时接收多个客户端消息

  1. server.py 中的内容
import socket
import time

sk = socket.socket()
sk.bind(("127.0.0.1", 8000))
sk.listen()

# 在 accept 之前加上循环,能够让我们和多个客户端进行沟通
# 注意与上面『带退出的聊天程序』中的循环的区别
while 1:
    # 接收一个客户端的请求
    conn, addr = sk.accept()
    print(addr)

    msg = conn.recv(1024)
    print("接收到客户端传来的消息:%s" % msg)

    format_msg = time.strftime(msg.decode())
    conn.send(format_msg.encode())
    # 断开一个客户端的请求
    conn.close()

sk.close()

2. client.py 中的内容

import socket

sk = socket.socket()
sk.connect(("127.0.0.1", 8000))

sk.send(b"%Y-%m-%d %H:%M:%S")

msg = sk.recv(1024).decode()
print("接收到服务端传来的消息:%s" % msg)

sk.close()

3. 先运行 server.py,再重复运行 client.py,发现 server.py 的运行结果会不断刷新,不会报错

# server.py:
(base) yanfa@yanfa-H110SD3-C:~/Desktop$ python server.py 
('127.0.0.1', 46500)
接收到客户端传来的消息:b'%Y-%m-%d %H:%M:%S'
('127.0.0.1', 46502)
接收到客户端传来的消息:b'%Y-%m-%d %H:%M:%S'
('127.0.0.1', 46504)
接收到客户端传来的消息:b'%Y-%m-%d %H:%M:%S'
('127.0.0.1', 46506)
接收到客户端传来的消息:b'%Y-%m-%d %H:%M:%S'
('127.0.0.1', 46508)
接收到客户端传来的消息:b'%Y-%m-%d %H:%M:%S'


# client.py:
(base) yanfa@yanfa-H110SD3-C:~/Desktop$ python client.py 
接收到服务端传来的消息:2019-11-25 18:05:58

(base) yanfa@yanfa-H110SD3-C:~/Desktop$ python client.py 
接收到服务端传来的消息:2019-11-25 18:06:00

(base) yanfa@yanfa-H110SD3-C:~/Desktop$ python client.py 
接收到服务端传来的消息:2019-11-25 18:06:01

(base) yanfa@yanfa-H110SD3-C:~/Desktop$ python client.py 
接收到服务端传来的消息:2019-11-25 18:06:02

(base) yanfa@yanfa-H110SD3-C:~/Desktop$ python client.py 
接收到服务端传来的消息:2019-11-25 18:06:03
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值