TCP通信

TCP通信

1.前期回顾

1.1 IP地址

IP地址标识电脑,端口标识程序

1.2 socket套接字

一种通信手段

套接字用完一定要关闭

sendto 两个参数内容为bytes类型的,IP是字符串,端口是数字

recvfrom()括号里面加1024的倍数,表示接收长度

绑定端口:bind()里面加元组,IP加端口

IP为空表示绑定本机的所有ip,如果填了某一个IP表示绑定了某一张网卡,只能接受这一网卡的消息。但是127.0.0.1绑定后,只能是本机给本机发送消息(其他人是发送不了的,因为此IP表示的是自己的电脑)

2.TCP

2.1 学习目标

理解UDP与TCP两种套接字的 不同点

理解send  recv函数的功能

理解服务器中bind  listen  accpet 函数的功能

2.2 重要的小点,无序,乱

  • 优秀的软件背后都有一个服务器,来支撑客户端的服务

  • 一般的服务器指的是硬件,编程里面的服务器指的是程序(软件),称为服务器应用程序

  • 客户端是享受服务的,服务器是提供服务的

  • 两种电话:总机(接受呼叫转接到分机)和分机(和客户进行通信)

  • 总机一般是不会停机的

  • 等待服务区:已经拨通,但是还没有被服务过的"listen(128)"代表的是最大有128个等待的

  • listen是将主动--->被动接听(listen可以将创建出来的主动套接字变为被动的,这是做TCP服务器时必须要做的,只有被动接听模式才能接受请求)

  • 远程拷贝

    scp -r 目标用户名@目标主机IP地址:/目标文件的绝对路径  /保存到本机的绝对/相对路径
    

    拷贝单个文件可以不加-r拷贝木录时必须加

    本地文件到远程(是在本地的桌面进行,不要ssh到Ubuntu)

    scp 123.txt python@192.168.33.113:~/Desktop/

    本地目录到远程

    scp -r FolderName RemoteUserName@RemoteHostIp:RemoteFolder
    scp -r FolderName RemoteHostIp:RemoteFolder

    远程文件到复制到本地(是在本地的桌面进行,不要ssh到Ubuntu)

    scp python@192.168.33.113:~/Desktop/123.txt ./

    远程目录到本地

    scp -r RemoteUserName@RemoteHostIp:RemoteFolder FolderName
    scp -r RemoteHostIp:RemoteFolder FolderName
  • 服务器的流程是固定的

    bind  --> listen ---> accept

2.3 TCP简介

简称:传输控制协议(Transmission Control Protocol)

特点:面向连接、可靠传输、*基于字节流(讲完之后就明白了,暂时忽略)

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

使用场景:HTTP  /  HTTPS   /  FTP

QQ文件传输

浏览器

面向连接

建立链接/通信/关闭链接

可靠传输

应答机制ACK

超时重传

错误校验,奇偶校验(00010110最后一个“0”前面每一位看看加起来是不是原 先的奇数或偶数)

拥塞控制   根据实时情况调整发送速度

*基于字节流

最小单位是字节     可能造成的现象:多次发送一次接收的线程

这次没有接收完的(超过了最大长度,超出的下次接收)会在下次接收,并不会消失

TCP对比UDP(重点)

可靠               --不可靠

面向连接       --面向无连接

保证顺序       --不保证发送包的顺序和接收顺序是一致的

不支持广播   --UDP支持广播

字节流协议   --用户数据报协议

2.4 TCP客户端

 1import socket
 2# 两个参数  第二个是字节流类型
 3tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4# 创建和服务器的连接  参数是服务器地址(ip,端口)
 5data = input("请输入你要发送的内容:")
 6ip = input("ip:")
 7port = int(input("端口:"))
 8
 9tcp_socket.connect((ip,port))
10
11
12# send(bytes类型数据)
13tcp_socket.send(data.encode())
14tcp_socket.close()
1#接收数据
2recv_data = tcp_socket.recv(1024)
3print(recv_data.decode())
  • 接收数据  阻塞等待数据  recv返回值一般情况下就是对方发的数据;如果对方断开了链接  返回值为“ ”

1#接收数据
2recv_data = tcp_socket.recv(1024)
3# if recv_data == b'':
4if not recv_data:
5   print("对方断开了连接,,,,,,,,")
6else:
7   print(recv_data.decode('gbk'))

2.5 数据的收发(重点)

创建 套接字对象 = socket(AF_INET,SOCK_STREAM)

链接 套接字对象.connect((服务器IP,端口))

IP与端口是一个元组,记得写括号

接收 bytes类型数据 = 套接字对象.recv(本次接收的最大长度)

如果接受的是来自Windows服务器的数据,需要转换成gbk格式

1recv_data.decode('gbk')

只有数据,没有地址,因为一开始就建立了链接

如果对方断开了链接  数据为空字节b' '

否则就是  通信的数据

发送 套接字对象.send(data.encode())

括号里为bytes类型的数据

关闭 套接字对象.close()

2.6 TCP服务器

2.6.1TCP给1个客户端服务
  • 如果我的服务器想被别人发现,那么就需要绑定bind

  • listen将主动变为被动,只有被动才能被接收

  • accept将等待区的用户接到分机

    • 上面的每次只能服务一个客服端

    • 遗留问题Adress already in use

 1import socket
 2
 3
 4# 1 总机 - 创建TCP套接字<服务器套接字 监听套接字>
 5server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 6
 7# 2 固定号码 - 固定端口
 8server_socket.bind(('', 10010))
 9
10# 3 安装客户服务系统 -  主动-> 被动监听模式
11server_socket.listen(128) #128表示有128个接通但是没没被服务过的
12
13# 4 从等待服务区取出一个客户端用以服务 转接到分机 - 接受连接   accept 接受连接
14# (和客户端关联起来的套接字对象<socket.socket>, 客户端套接字地址('192.168.33.110', 46080))
15client_socket, client_address = server_socket.accept()
16print("接受到了来自%s的连接请求" % str(client_address))
17
18while True:
19   # 5 使用分机进行深入交流 echo回射
20   recv_data = client_socket.recv(1024)
21   print("接收到了数据:%s" % recv_data.decode())
22   client_socket.send(recv_data)#回射服务器
23   if not recv_data:
24       print("客户端下线了")
25       break
26
27# 6 分机挂机
28client_socket.close()
29# 7 总机挂机
30server_socket.close()
2.6.2TCP给多个客户端服务

改进代码

 1import socket
 2
 3
 4# 1 总机 - 创建TCP套接字<服务器套接字 监听套接字>
 5server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 6
 7# 2 固定号码 - 固定端口
 8server_socket.bind(('', 10010))
 9
10# 3 安装客户服务系统 -  主动-> 被动监听模式
11server_socket.listen(128)
12
13while True:
14   # 4 从等待服务区取出一个客户端用以服务 转接到分机 - 接受连接
15   # (和客户端关联起来的套接字对象<socket.socket>, 客户端套接字地址('192.168.33.110', 46080))
16   client_socket, client_address = server_socket.accept()
17   print("接受到了来自%s的连接请求" % str(client_address))
18
19   while True:
20       # 5 使用分机进行深入交流 echo回射
21       recv_data = client_socket.recv(1024)
22       print("接收到了数据:%s" % recv_data.decode('gbk'))
23       client_socket.send(recv_data.decode('gbk').encode())
24       if not recv_data:
25           print("客户端下线了")
26           break
27
28   # 6 分机挂机
29   client_socket.close()
30
31# 7 总机挂机
32server_socket.close()

2.7  案例(文件下载客户端)

百度网盘、邮件等都是TCP的例子

 1import socket
 2# 1.建立和服务器的连接
 3# 1.1用户输入ip地址和端口
 4IP = input("服务器ip:")
 5port =int( input("服务器端口:"))
 6# 1.2创建tcp的套接字
 7tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 8# 1.3连接服务器
 9tcp_socket.connect((IP,port))
10# 2.向服务器发送需要下载的文件名称
11# 2.1用户输入文件名称
12file_name = input("需要下载的文件名称:")
13# 2.2发送
14tcp_socket.send(file_name.encode())
15# 3.一边接收文件数据,一边写入文件   完后:关闭套接字  文件
16# 3.1打开文件用于保存  接收到的数据(文件的大小一般是1024或整数倍
17#                                    ,太大没用,网卡有限制,4k/16左右就可以了)
18file = open("下载" + file_name,"wb")  # 为了减少代码,方便统一,全为二进制
19while True:
20   # 3.2接收数据,写入文件
21   file_data = tcp_socket.recv(4096)
22   #  3.3如果数据是‘’传输完成  关闭文件  套接字   ; 否则继续3.2步骤
23   if not file_data:
24       print("文件下载完成.....")
25       file.close()
26       tcp_socket.close()
27       break
28   file.write(file_data)

2.8 案例(文件下载服务器)

流程:

1创建服务器套接字  --->2 绑定端口   监听 ----> 3接受链接请求   ----> 4接受到来自客户端的文件名   ----> 5根据文件名读取文件数据   ---->  6发送文件数据   ----> 7关闭文件 ----> 8关闭套接字  ----> 9再执行第4步

 1import socket
 2def main():
 3   # 1 接受用户的连接请求
 4   # 1.1 创建服务器套接字 -- 接受请求,将请求转接到客户端关联的套接字上去
 5   server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 6   # 1.2 绑定端口  让别人发现我们
 7   server_socket.bind(('', 1111))
 8   # 1.3 监听
 9   server_socket.listen(128)  # 128是一个经验值,记住,常用
10   while True:
11       # 1.4 接受连接  返回值是元祖(客户端关联的套接字对象,客户端地址)
12       client_socket, client_address = server_socket.accept()
13       print("接受到来自%s的连接请求" % str(client_address))
14       # 2 接收文件名称 -
15       # 2.1使用和客户端关联的套接字对象  接收数据
16       file_name = client_socket.recv(4096)
17       if not file_name:
18           print("客户端断开连接......")
19           client_socket.close()
20           continue
21       # 2.2文件名解码称str类型
22       file_name = file_name.decode()
23       #  3 根据文件名称 读取文件数据 发送给客户端
24       # 3.1打开指定文件名称的文件  用以读取
25       with open(file_name,"rb") as file:# 读完之后交给with自动关闭,记住语法
26
27           # 3.2(边读边发)这里比较小,直接读取出整个文件的数据
28           file_data = file.read()  # 如果文件大,可能存在程序奔溃的风险
29           # file.close()
30           # 3.3 将数据发送给客户端
31           client_socket.send(file_data)
32       #  4 如果传输完成 关闭文件,关闭套接字
33       client_socket.close()
34if __name__ == '__main__':
35   main()

2.9 了解TCP的三次握手

三次握手目的:建立链接

SYN同步请求--建立链接的请求                           seq代表序号

ACK应答

ACK再次应答

SYN=1     1代表这个包有同步请求的功能   大写的ACK代表着标志,标志之一代表设置

ack=J + 1   代表收到了

对于服务器必须三次握手,已经完成三次握手的连接,称为全连接

未完成三次握手的连接,称为半连接

listen可以实现将半连接和全连接分成两类,分别存放在不同的空间

listen(128)作用

1 主动 -> 监听

2 参数含义,Linux中表示已就绪队列长度,其他平台一般表示二者综合

(128单位是个,表示最多容纳128个客户端,其他人再链接就连不上了)

accept作用

从已就绪队列中取出一个全连接用以通信

connect作用

发起并且完成和服务器之间的三次握手    建立连接

2.9 了解TCP的4次挥手

FIN表示完成

除非双方都断开才能完成断开链接,只有一端断开是完不成的

主动端和被动端

意义:忽略2MSL时间(进行补充)

目的:解决前面历史遗留问题

现象:服务器立即重启出现。Address in use地址使用中

表面原因:TCP标准规定了  凡是主动断开连接的一方,必须保持该连接资源一端时间2MSL而不被释放    2MSL时间:30s到2min

深层原因:为了更加彻底断开TCP连接

最后一次ACK可能会丢失

       解决问题:使用地址重用选项--忽略2MSL时间

1套接字对象.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

3.显示时间

先导入time模块

1time.strftime('%Y-%m-%d %H:%M:%S')

上面的结果可以直接使用字符串进行接收

3.1 智能机器人客户端

 1import socket
 2tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 3tcp_socket.connect(("192.168.191.5",10086))
 4print("欢迎使用TCP通信软件".center(36,'='))
 5print("小闫出品,必属精品".center(35,'-'))
 6
 7while True:
 8   b = input("请输入你要问的问题:")
 9   if not b:
10       print("欢迎下次使用")
11       break
12   tcp_socket.send(b.encode())
13   a = tcp_socket.recv(1024)
14   print("智能机器人回复您:%s" % a.decode())
15   print('='*40)

3.2 智能机器人服务器

 1import socket
 2
 3import time
 4
 5"""
 61.自己写tcp客户端以及服务端
 72.发送"你好"、"hello"等,会回复"你好"
 83.发送"名字"、"name"等关键词,会回复"我是python29号"
 94.发送"时间"、"time"等关键词,会回复"当前时间是:xx:xx:xx(当前时间)"
10   提示:显示时间:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
11        可简写为:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
12        可简写为:time.strftime('%Y-%m-%d %H:%M:%S')
13……自行添加
14"""
15server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
16server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
17server_socket.bind(('',10086))
18server_socket.listen(128)
19client_socket,client_adress = server_socket.accept()
20print("%s连接了您" % str(client_adress))
21while True:
22
23   new_data = client_socket.recv(1024)
24   data = new_data.decode()
25   if "你好" in data or"hello" in data:
26       client_socket.send('你好'.encode())
27   elif "名字" in data or"name" in data:
28       client_socket.send('我是python29号'.encode())
29   elif "时间" in data or "time" in data:
30       client_socket.send(time.strftime('%Y-%m-%d %H:%M:%S').encode())
31   elif not data:
32       print("下线")
33       break
34   else:
35       client_socket.send("我不能明白您的意思,好尴尬啊...".encode())
36client_socket.close()

3.3 尬聊机器人

 1import socket
 2import random
 3
 4"""
 5实现局域网内的点对点聊天机器人程序。
 6使用TCP协议编写 socket 程序,分别实现消息的发送端和接收端
 7服务端记录客户端发送的消息,并进行随机回复
 8当客户端发送Bye时结束聊天
 9"""
10def main():
11   tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
12   tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
13   tcp_socket.bind(('',10086))
14   tcp_socket.listen(128)
15   list = []
16   while True:
17       client_socket,client_address = tcp_socket.accept()
18
19       print("与%s建立连接...." % str(client_address))
20       while True:
21           data = client_socket.recv(1024)
22           new_data = data.decode()
23           list.append(new_data)
24           n = random.randint(0,(len(list)-1))
25           client_socket.send(list[n].encode())
26           if not data:
27               print("客户端断开连接...")
28               break
29       client_socket.close()
30
31if __name__ == '__main__':
32   main()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值