前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >TCP通信

TCP通信

作者头像
小闫同学啊
发布于 2019-07-18 03:21:55
发布于 2019-07-18 03:21:55
1.1K00
代码可运行
举报
文章被收录于专栏:小闫笔记小闫笔记
运行总次数:0
代码可运行

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 学习目标

理解UDPTCP两种套接字的 不同点

理解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客户端

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import socket
# 两个参数  第二个是字节流类型
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 创建和服务器的连接  参数是服务器地址(ip,端口)
data = input("请输入你要发送的内容:")
ip = input("ip:")
port = int(input("端口:"))

tcp_socket.connect((ip,port))


# send(bytes类型数据)
tcp_socket.send(data.encode())
tcp_socket.close()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#接收数据
recv_data = tcp_socket.recv(1024)
print(recv_data.decode())
  • 接收数据 阻塞等待数据 recv返回值一般情况下就是对方发的数据;如果对方断开了链接 返回值为“ ”
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#接收数据
recv_data = tcp_socket.recv(1024)
# if recv_data == b'':
if not recv_data:
   print("对方断开了连接,,,,,,,,")
else:
   print(recv_data.decode('gbk'))

2.5 数据的收发(重点)

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

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

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
recv_data.decode('gbk')

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

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

否则就是 通信的数据

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

括号里为bytes类型的数据

关闭 套接字对象.close()

2.6 TCP服务器

2.6.1TCP给1个客户端服务
  • 如果我的服务器想被别人发现,那么就需要绑定bind
  • listen将主动变为被动,只有被动才能被接收
  • accept将等待区的用户接到分机
    • 上面的每次只能服务一个客服端
    • 遗留问题Adress already in use
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import socket


# 1 总机 - 创建TCP套接字<服务器套接字 监听套接字>
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2 固定号码 - 固定端口
server_socket.bind(('', 10010))

# 3 安装客户服务系统 -  主动-> 被动监听模式
server_socket.listen(128) #128表示有128个接通但是没没被服务过的

# 4 从等待服务区取出一个客户端用以服务 转接到分机 - 接受连接   accept 接受连接
# (和客户端关联起来的套接字对象<socket.socket>, 客户端套接字地址('192.168.33.110', 46080))
client_socket, client_address = server_socket.accept()
print("接受到了来自%s的连接请求" % str(client_address))

while True:
   # 5 使用分机进行深入交流 echo回射
   recv_data = client_socket.recv(1024)
   print("接收到了数据:%s" % recv_data.decode())
   client_socket.send(recv_data)#回射服务器
   if not recv_data:
       print("客户端下线了")
       break

# 6 分机挂机
client_socket.close()
# 7 总机挂机
server_socket.close()
2.6.2TCP给多个客户端服务

改进代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import socket


# 1 总机 - 创建TCP套接字<服务器套接字 监听套接字>
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2 固定号码 - 固定端口
server_socket.bind(('', 10010))

# 3 安装客户服务系统 -  主动-> 被动监听模式
server_socket.listen(128)

while True:
   # 4 从等待服务区取出一个客户端用以服务 转接到分机 - 接受连接
   # (和客户端关联起来的套接字对象<socket.socket>, 客户端套接字地址('192.168.33.110', 46080))
   client_socket, client_address = server_socket.accept()
   print("接受到了来自%s的连接请求" % str(client_address))

   while True:
       # 5 使用分机进行深入交流 echo回射
       recv_data = client_socket.recv(1024)
       print("接收到了数据:%s" % recv_data.decode('gbk'))
       client_socket.send(recv_data.decode('gbk').encode())
       if not recv_data:
           print("客户端下线了")
           break

   # 6 分机挂机
   client_socket.close()

# 7 总机挂机
server_socket.close()

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

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

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

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

流程:

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import socket
def main():
   # 1 接受用户的连接请求
   # 1.1 创建服务器套接字 -- 接受请求,将请求转接到客户端关联的套接字上去
   server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   # 1.2 绑定端口  让别人发现我们
   server_socket.bind(('', 1111))
   # 1.3 监听
   server_socket.listen(128)  # 128是一个经验值,记住,常用
   while True:
       # 1.4 接受连接  返回值是元祖(客户端关联的套接字对象,客户端地址)
       client_socket, client_address = server_socket.accept()
       print("接受到来自%s的连接请求" % str(client_address))
       # 2 接收文件名称 -
       # 2.1使用和客户端关联的套接字对象  接收数据
       file_name = client_socket.recv(4096)
       if not file_name:
           print("客户端断开连接......")
           client_socket.close()
           continue
       # 2.2文件名解码称str类型
       file_name = file_name.decode()
       #  3 根据文件名称 读取文件数据 发送给客户端
       # 3.1打开指定文件名称的文件  用以读取
       with open(file_name,"rb") as file:# 读完之后交给with自动关闭,记住语法

           # 3.2(边读边发)这里比较小,直接读取出整个文件的数据
           file_data = file.read()  # 如果文件大,可能存在程序奔溃的风险
           # file.close()
           # 3.3 将数据发送给客户端
           client_socket.send(file_data)
       #  4 如果传输完成 关闭文件,关闭套接字
       client_socket.close()
if __name__ == '__main__':
   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时间

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
套接字对象.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

3.显示时间

先导入time模块

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
time.strftime('%Y-%m-%d %H:%M:%S')

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

3.1 智能机器人客户端

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import socket
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
tcp_socket.connect(("192.168.191.5",10086))
print("欢迎使用TCP通信软件".center(36,'='))
print("小闫出品,必属精品".center(35,'-'))

while True:
   b = input("请输入你要问的问题:")
   if not b:
       print("欢迎下次使用")
       break
   tcp_socket.send(b.encode())
   a = tcp_socket.recv(1024)
   print("智能机器人回复您:%s" % a.decode())
   print('='*40)

3.2 智能机器人服务器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import socket

import time

"""
1.自己写tcp客户端以及服务端
2.发送"你好""hello"等,会回复"你好"
3.发送"名字""name"等关键词,会回复"我是python29号"
4.发送"时间""time"等关键词,会回复"当前时间是:xx:xx:xx(当前时间)"
   提示:显示时间:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))
        可简写为:time.strftime('%Y-%m-%d %H:%M:%S',time.localtime())
        可简写为:time.strftime('%Y-%m-%d %H:%M:%S')
……自行添加
"""
server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
server_socket.bind(('',10086))
server_socket.listen(128)
client_socket,client_adress = server_socket.accept()
print("%s连接了您" % str(client_adress))
while True:

   new_data = client_socket.recv(1024)
   data = new_data.decode()
   if "你好" in data or"hello" in data:
       client_socket.send('你好'.encode())
   elif "名字" in data or"name" in data:
       client_socket.send('我是python29号'.encode())
   elif "时间" in data or "time" in data:
       client_socket.send(time.strftime('%Y-%m-%d %H:%M:%S').encode())
   elif not data:
       print("下线")
       break
   else:
       client_socket.send("我不能明白您的意思,好尴尬啊...".encode())
client_socket.close()

3.3 尬聊机器人

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import socket
import random

"""
实现局域网内的点对点聊天机器人程序。
使用TCP协议编写 socket 程序,分别实现消息的发送端和接收端
服务端记录客户端发送的消息,并进行随机回复
当客户端发送Bye时结束聊天
"""
def main():
   tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
   tcp_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
   tcp_socket.bind(('',10086))
   tcp_socket.listen(128)
   list = []
   while True:
       client_socket,client_address = tcp_socket.accept()

       print("与%s建立连接...." % str(client_address))
       while True:
           data = client_socket.recv(1024)
           new_data = data.decode()
           list.append(new_data)
           n = random.randint(0,(len(list)-1))
           client_socket.send(list[n].encode())
           if not data:
               print("客户端断开连接...")
               break
       client_socket.close()

if __name__ == '__main__':
   main()
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-12-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 全栈技术精选 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Go语言学习系列——条件语句:if-else、switch——【坚果派-红目香薰】
if-else: 1. 条件表达式不需要括号 2. 大括号必须有,且必须在同一行 3. 支持初始化语句 4. 条件必须是布尔值 switch: 1. 不需要break语句 2. 支持多条件匹配 3. 支持无条件表达式的形式 4. fallthrough关键字用于继续执行下一个case 5. case后可以是表达式 6. default可选,位置灵活
红目香薰
2025/03/13
960
Go语言学习系列——条件语句:if-else、switch——【坚果派-红目香薰】
Go 语句
if...else 语句 | if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。 |
acc8226
2022/05/17
3900
go 流程控制之switch 语句介绍
我们先通过一个例子来直观地感受一下 switch 语句的优点。在一些执行分支较多的场景下,使用 switch 分支控制语句可以让代码更简洁,可读性更好。
贾维斯Echo
2023/10/18
3410
go语言慢速入门——流程控制语句
go的流程控制语句很有特色。if-else,for,switch-case。注意go没有while和do-while语句。除此之外go还有和特定类型绑定的流程控制模块。例如,用于容器类型的for-range循环。 go支持break,continue以及goto语句,另外go还支持特有的fallthrough语句。
zy010101
2022/07/28
4310
11.Go语言-流程控制
所谓流程控制就是指“程序怎么执行”或者说“程序执行的顺序”。程序整体上确实是从上往下执行,但又不单纯是从上往下。
面向加薪学习
2022/09/04
5540
第18节 switch语句
switch是一个条件语句,它计算表达式并将其与可能匹配的列表进行比较,并根据匹配执行代码块。它可以被认为是一种惯用的方式来写多个if else子句。
小尘哥
2019/07/19
6220
Golang语言社区--Go语言基础第五节流程控制
大家好,我是Golang语言社区(www.Golang.LTD)主编彬哥,今天给大家带来的是关于go语言中的流程控制。
李海彬
2018/03/08
7920
Golang语言社区--Go语言基础第五节流程控制
Go语言中的控制结构:条件语句和循环
在编程中,控制结构是指引导程序如何执行的一系列指令。Go语言提供了多种控制结构来处理条件判断和循环操作。本文将详细介绍Go语言中的条件语句和循环结构,通过实例和代码解释,帮助读者全面理解和应用这些基本构造。
数字扫地僧
2024/06/12
1520
【愚公系列】2022年7月 Go教学课程 018-分支结构之switch
Switch在一些计算机语言中是保留字,其作用大多情况下是进行判断选择。以Go语言来说,switch(开关语句)常和case default一起使用。
愚公搬代码
2022/08/01
4640
【愚公系列】2022年7月 Go教学课程 018-分支结构之switch
Go 语言基础入门教程 —— 流程控制篇:分支语句
分支语句会根据传入条件的不同,选择不同的分支代码执行。Go 语言的分支语句和 PHP 类似,只是不需要在每个分支结构中显式通过 break 语句退出:
学院君
2019/08/08
5500
100天精通Golang(基础入门篇)——第8天:Go语言程序的流程结构和条件语句
本文是《100天精通Golang(基础入门篇)》系列的第8天,介绍了Golang中程序的流程结构和条件语句。通过学习程序的流程结构,包括顺序结构、分支结构和循环结构,以及条件语句如if语句和switch语句,读者可以进一步掌握Golang的基础知识和语法。
猫头虎
2024/04/08
1590
100天精通Golang(基础入门篇)——第8天:Go语言程序的流程结构和条件语句
Go语言入门:分支结构
注意:其他很多编程语言是要求if后的条件判断表达式要用()括号括起来,但Go语言不需要!
阿珍
2025/02/06
670
Go语言入门:分支结构
Go语言条件语句
Go语言的条件语句在概念和流程上与其他编程语言完全相同,因此这里不再赘述,写法上结合了Python和C++,即条件表达式不需要加括号,但仍然保留了花括号。具体例子见下:
Steve Wang
2020/12/22
5870
三分钟学 Go 语言——条件语句+switch和type switch
switch 好理解,是一个替代if else else else接口而提出的,如下,switch 后跟变量,case 后跟常量,只要变量值和常量匹配,就执行该分支下的语句。
机智的程序员小熊
2020/04/21
5720
三分钟学 Go 语言——条件语句+switch和type switch
GO语言基础之条件语句if
如果condition为true时,执行上面{ 代码 } 之间的代码,如果为false时,执行else里面的代码
墨紫羽墨
2022/01/12
6960
Go通关03:控制结构,if、for、switch逻辑语句
您诸位好啊,我是无尘,今天学习下go语言的控制结构: if 条件语句 func main() { i:=6 if i >10 { fmt.Println("i>10") } else if i>5 && i<=10 { fmt.Println("5<i<=10") } else { fmt.Println("i<=5") } } 注意: if 后的表达无 ‘( )’ 每个条件分支中的 ‘{ }’ 是必须的。哪怕只
微客鸟窝
2021/08/18
3720
【Go语言刷题篇】Go从0到入门5:Map综合复习、条件语句、循环语句练习
问题描述:给定两个字符串des 和src ,判断 des能不能由 src 里面的字符构成,//如果可以,返回 true ;否则返回 false,src中的每个字符只能在 des 中使用一次。
程序员洲洲
2024/06/07
1340
【Go语言刷题篇】Go从0到入门5:Map综合复习、条件语句、循环语句练习
Go 语言学习之流程控制
在 Go 语言中,if...else... 语句的条件表达式必须是布尔类型,可以省略小括号,并且左大括号不能另起一行。通过代码,我们演示 if...else... 的标准使用方法。
frank.
2020/07/28
2690
Go 编程 | 连载 09 - 条件和循环表达式
需要注意的是 Go 中 if 控制语句的 { 不可以换行,必须要跟 if 关键字在同一行,否则会报错。
RiemannHypothesis
2022/09/26
2350
Go 编程 | 连载 09 - 条件和循环表达式
go语言基础3-控制语句
if 实例 func method(v int) int { if v >100 { return 100 }else if v < 0 { return 0 }else { return v } } //读取文件 func readFile(){ const filename = "branch/abc.txt" if constens, err := ioutil.ReadFile(filename); err
吐吐吐吐吐葡萄皮
2019/04/04
3660
go语言基础3-控制语句
推荐阅读
相关推荐
Go语言学习系列——条件语句:if-else、switch——【坚果派-红目香薰】
更多 >
目录
  • TCP通信
    • 1.前期回顾
      • 1.1 IP地址
      • 1.2 socket套接字
    • 2.TCP
      • 2.1 学习目标
      • 2.2 重要的小点,无序,乱
      • 2.3 TCP简介
      • 2.4 TCP客户端
      • 2.5 数据的收发(重点)
      • 2.6 TCP服务器
      • 2.7 案例(文件下载客户端)
      • 2.8 案例(文件下载服务器)
      • 2.9 了解TCP的三次握手
      • 2.9 了解TCP的4次挥手
    • 3.显示时间
      • 3.1 智能机器人客户端
      • 3.2 智能机器人服务器
      • 3.3 尬聊机器人
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档