07.网络编程-3.TCP
1、tcp相关介绍
TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。
TCP通信需要经过创建连接、数据传送、终止连接三个步骤。
TCP通信模型中,在通信开始之前, 一定要先建立相关的链接, 才能发送数据, 类似于生活中, "打电话""
TCP特点
- 面向连接
通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。
双方间的数据传输都可以通过这一个连接进行。
完成数据交换后,双方必须断开此连接,以释放系统资源。
这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。
可靠传输
- TCP采用发送应答机制
TCP发送的每个报文段都必须得到接收方的应答才认为这个TCP报文段传输成功
- 超时重传
发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。
TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。
- 错误校验
TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
- 流量控制和阻塞管理
流量控制用来避免主机发送得过快而使接收方来不及完全收下。
TCP与UDP的不同点
- 面向连接(确认有创建三方交握,连接已创建才作传输。)
- 有序数据传输
- 重发丢失的数据包
- 舍弃重复的数据包
- 无差错的数据传输
- 阻塞/流量控制
2、TCP服务器
实际生活中:
- 买个手机
- 插上手机卡
- 设计手机为正常接听状态(即能够响铃)
- 静静的等着别人拨打
在程序中, 如果想要完成一个TCP服务器的功能, 需要的流程如下:
- socket创建一个套接字
- bind绑定ip和port
- listen使套接字变为可以被动链接
- accept等待客户端的链接
- recv/send接收发送数据
一个很简单的TCP服务器如下:
from socket import *
# 1.创建一个socket套接字(买手机卡)
tcpSerSocket = socket(AF_INET, SOCK_STREAM)
# 2.bind绑定本机ip和port(插上手机卡)
address = (''. 7777)
tcpSerSocket.bind(address)
# 3.listen使套接字变为可以被动链接(设置为手机正常接听状态)
tcpSerSocket.listen(5)
# 4.accept等待客户端的链接(静静的等着别人拨打)
# newSocket用来为这个客户端服务
# tcpSerSocket就可以省下来专门等待其他新客户端的链接
newSocket, clientAddr = tcpSerSocket.accept()
# 5.recv接收数据,最大接收1024个字节
recvData = newSocket.recv(1024).decode('gbk')
print('接收到的数据为:',recvData)
# 6.send发送一些数据到客户端
newSocket.send("服务器已经收到了!".encode('gbk'))
# 关闭为这个客户端服务的套接字, 只要关闭了, 就意味着为不能再为这个客户端服务
newSocket.close()
# 关闭监听套接字, 只要这个套接字关闭了, 就意味着整个程序不能再接收任何新的客户端的连接
tcpSerSocket.close()
3、TCP客户器
TCP的客户端要比服务器端简单很多,如果说服务器端是需要自己买手机、查手机卡、设置铃声、等待别人打电话流程的话,那么客户端就只需要找一个电话亭,拿起电话拨打即可,流程要少很多
生活中的电话机,如果想让别人能更够打通咱们的电话获取相应服务的话,需要做以下几件事情:
- 买个手机
- 插上手机卡
- 设计手机为正常接听状态(即能够响铃)
- 静静的等着别人拨打
from socket import *
from time import *
# 1.socket创建一个套接字
tcpClient = socket(AF_INET,SOCK_STREAM)
# 2.绑定本机端口号,也可以不绑定
tcpClient.bind(('',8888))
# 3.连接服务器
tcpClient.connect(('192.168.14.72',5678))
#4.接收消息
data1 = tcpClient.recv(1024)
print(data1.decode('gbk'))
#5.发消息
while True:
data = input('>>\t').encode('gbk')
tcpClient.send(data)
if data=='886':
sleep(4)
tcpClient.close()
break
4.应用:模拟QQ聊天
客户端参考代码
from socket import *
# 创建socket
tcpClientSocket = socket(AF_INET, SOCK_STREAM)
# 链接服务器
serAddr = ('192.168.1.102', 7788)
tcpClientSocket.connect(serAddr)
while True:
# 提示用户输入数据
sendData = input("send:")
if len(sendData)>0:
tcpClientSocket.send(sendData)
else:
break
# 接收对方发送过来的数据,最大接收1024个字节
recvData = tcpClientSocket.recv(1024)
Print('recv:',recvData)
# 关闭套接字
tcpClientSocket.close()
服务器端参考代码
#coding=utf-8
from socket import *
# 创建socket
tcpSerSocket = socket(AF_INET, SOCK_STREAM)
# 绑定本地信息
address = ('', 7788)
tcpSerSocket.bind(address)
# 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了
tcpSerSocket.listen(5)
while True:
# 如果有新的客户端来链接服务器,那么就产生一个信心的套接字专门为这个客户端服务器
# newSocket用来为这个客户端服务
# tcpSerSocket就可以省下来专门等待其他新客户端的链接
newSocket, clientAddr = tcpSerSocket.accept()
while True:
# 接收对方发送过来的数据,最大接收1024个字节
recvData = newSocket.recv(1024)
# 如果接收的数据的长度为0,则意味着客户端关闭了链接
if len(recvData)>0:
print 'recv:',recvData
else:
break
# 发送一些数据到客户端
sendData = input("send:")
newSocket.send(sendData)
# 关闭为这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接
newSocket.close()
# 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接
tcpSerSocket.close()
5.应用:tcp通信-多线程服务器端
from socket import *
from threading import *
class Mythread(Thread):
def __init__(self, socketClient, clientAddress):
Thread.__init__(self)
self.socketClient = socketClient
self.clientAddress = clientAddress
def run(self):
data1 = '哈哈。。。可以聊天了'.encode('utf_8')
self.socketClient.send(data1)
while True:
try:
# 接收客户端发的消息
data = self.socketClient.recv(1024).decode('utf-8')
except:
print('<<\t%s-%s 已经断开了' % (self.clientAddrdess[0], self.clientAddrdess[1]))
break
if data == '886':
break
print('<<\t%s-%s:%s' % (self.clientAddrdess[0], self.clientAddrdess[1], data))
def main():
tcpServer = socket(AF_INET, SOCK_STREAM)
tcpServer.bind(('', 5678))
tcpServer.listen(5)
while True:
print('服务器等待客户端连接......')
socketClient, clientAddress = tcpServer.accept()
Mythread(socketClient, clientAddress).start()
print('%s-%s 连接成功......' % (clientAddrdess[0], clientAddrdess[1]))
if __name__ == '__main__':
main()
#tcpServer.close() 通常不用关服务器的
6、tcp三次握手
- 当客户端调用connect时,发送syn x(一个序号)给服务端,服务端有listen和accept,服务端收到后ack x+1给客户端,同时会一并传递一个新的syn y,客户端收到后,acd y+1给服务端,此时三次握手完成。
- 生活中的例子理解以上内容:好比AB两人打电话,A发出hello后,不清楚B是否收到信息,B收到后回复听到 了,并发送hello,A收到后确定第一次发出的信息B可以收到,但是B发送信息后,无法确定A是否收到,因此A应当回复收到
7、tcp四次挥手
- 通过close关闭套接字来实现四次挥手,一般情况下,服务器不会主动关闭,而是让客户端关闭连接。以下内容假定以客户端关闭socket来描述,指定A为客户端,B为服务器
- socket是全双工的,可以同时接收和发送
- 当A调用close后,自身关闭send(socket依然存在),并告知B(第一次)不会再发送内容了,B收到后,返回消息给A(第二次)表明已收到,就会去关闭recv,此时的实现是底层的,为了解除堵塞,B会设定recv得到的内容为空字符串,此时如果有代码判定,如果recv收到内容为空,就要调用close来关闭B的send,调用完成后,发送消息给A(第三次),A收到后关闭recv,并反馈消息给B(第四次),至此四次挥手完成,但是A的socket并未关闭,因为它不确定最后一次发送消息B是否收到,假设B没有收到,那么B会在超时等待(每次通信过程,都存在超时等待,超过等待时间,就会重新发送数据)后再次发送数据,此时如果A不存在了,此次过程就会失败,所以A会等待两个MSL(数据在网络中存储的最大时间,因为要发和收,所以是两个)的时间后才会关闭,这也就是出现地址占用问题的原因,解决办法见Address already in use
- 谁先调用close,谁就会在最后等待两分钟左右,所以会有端口占用问题,因为服务器会绑定端口,客户端不绑定端口,所以一般先让客户端close,客户端如果再次运行,又会随机分配端口
- 与三次握手,将服务器端的确认信息及反馈信息同时发送不同,四次挥手第二次和第三次无法合并,因为是不同的情况下发生的,第三次是需要服务器调用close才会发生的
8、tcp十种状态
注意:
- 当一端收到一个FIN,内核让read返回0来通知应用层另一端已经终止了向本端的数据传送
- 发送FIN通常是应用层对socket进行关闭的结果
9、tcp长连接和短连接
9.1TCP短连接
模拟一种TCP短连接的情况:
- client 向 server 发起连接请求
- server 接到请求,双方建立连接
- client 向 server 发送消息
- server 回应 client
- 一次读写完成,此时双方任何一个都可以发起 close 操作
在第 步骤5中,一般都是 client 先发起 close 操作。当然也不排除有特殊的情况。
从上面的描述看,短连接一般只会在 client/server 间传递一次读写操作!
9.2TCP长连接
再模拟一种长连接的情况:
- client 向 server 发起连接
- server 接到请求,双方建立连接
- client 向 server 发送消息
- server 回应 client
- 一次读写完成,连接不关闭
- 后续读写操作...
- 长时间操作之后client发起关闭请求
tcp注意点
- tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器
- tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机
- tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的
- 当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信
- 当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务
- listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的
- 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信。
- 关闭accept返回的套接字意味着这个客户端已经服务完毕
- 当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0(是空字符串),因此服务器可以通过返回数据的长度(或者判断是否是空字符串)来区别客户端是否已经下线
07.网络编程-3.TCP的更多相关文章
- C#网络编程之---TCP协议的同步通信(二)
上一篇学习日记C#网络编程之--TCP协议(一)中以服务端接受客户端的请求连接结尾既然服务端已经与客户端建立了连接,那么沟通通道已经打通,载满数据的小火车就可以彼此传送和接收了.现在让我们来看看数据的 ...
- 嵌入式linux的网络编程(1)--TCP/IP协议概述
嵌入式linux的网络编程(1)--TCP/IP协议概述 1.OSI参考模型及TCP/IP参考模型 通信协议用于协调不同网络设备之间的信息交换,它们建立了设备之间互相识别的信息机制.大家一定都听说过著 ...
- 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系
[Linux网络编程]TCP网络编程中connect().listen()和accept()三者之间的关系 基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: conn ...
- 【网络编程1】网络编程基础-TCP、UDP编程
网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...
- 【转载】[基础知识]【网络编程】TCP/IP
转自http://mc.dfrobot.com.cn/forum.php?mod=viewthread&tid=27043 [基础知识][网络编程]TCP/IP iooops 胖友们楼主我又 ...
- Java 网络编程 -- 基于TCP 模拟多用户登录
Java TCP的基本操作参考前一篇:Java 网络编程 – 基于TCP实现文件上传 实现多用户操作之前先实现以下单用户操作,假设目前有一个用户: 账号:zs 密码:123 服务端: public c ...
- python网络编程05 /TCP阻塞机制
python网络编程05 /TCP阻塞机制 目录 python网络编程05 /TCP阻塞机制 1.什么是拥塞控制 2.拥塞控制要考虑的因素 3.拥塞控制的方法: 1.慢开始和拥塞避免 2.快重传和快恢 ...
- 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程
Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...
- C#网络编程之--TCP协议(一)
TCP 是面向连接的传输协议 面向连接,其实就好比,A打电话给B,如果B接听了,那么A和B之间就的通话,就是面向连接的 TCP 是全双工的传输协议 全双工,这个理解起来也很简单,A打电话给B,B接听电 ...
随机推荐
- log_archive_dest_1设置报错
DG搭建完之后,又报错: Tue Dec 22 16:24:33 2015 Errors in file /u01/app/oracle/admin/orcl/bdump/orcl_arc1_2994 ...
- 哈理工2015暑假集训 zoj 2975 Kinds of Fuwas
G - Kinds of Fuwas Time Limit:2000MS Memory Limit:65536KB 64bit IO Format:%lld & %llu Subm ...
- spring与springboot中,如何在static方法里使用自动注入的属性
第一步:写注解@Component 使当前类成为一个bean对象.(@Controller,@service都行) 第二步:写个static的变量 第三步:写个@PostConstruct注解注解注释 ...
- C#上移,下移TreeView中的树节点顺序
C#上移,下移TreeView中的树节点顺序 2009-08-12 20:10 1494人阅读 评论(2) 收藏 举报 c#buttonobjectnullstring C#中,通过单击上移,下移按钮 ...
- HTTP权威协议笔记-10.HTTP-NG
1.HTTP发展中存在的问题 复杂性 其连接.报文.及功能逻辑之间的混合使用相当复杂,使用容易出错 可扩展性 传统流行下来的http应用很难实现扩展性,且无法兼容 性能 高延时.低吞吐 ...
- Vue开发入门看这篇文章就够了
摘要: 很多值得了解的细节. 原文:Vue开发看这篇文章就够了 作者:Random Fundebug经授权转载,版权归原作者所有. 介绍 Vue 中文网 Vue github Vue.js 是一套构建 ...
- Angular 显示英雄列表
在本页面,你将扩展<英雄指南>应用,让它显示一个英雄列表, 并允许用户选择一个英雄,查看该英雄的详细信息. 创建模拟(mock)英雄数据 你需要一些英雄数据以供显示. 最终,你会从远端的数 ...
- layui框架 各种小结
首先项目前端采用的是bootstrap和layui弹窗,验证,表格用的是bootstrapTable layui官方地址:http://www.layui.com/ 文档:http://www.lay ...
- Redis hash结构 和常用命令
Redis 数据结构 -- 哈希 hash 是 一个 String 类型的field 和 value 的映射表 hash 的键值 对在内存中的一种无序的状态 命令 说明 备注 hdel key fie ...
- Redis学习笔记(三)-数据类型之string类型
string是redis最基本的类型,而且string类型是二进制安全的.意思是redis的string可以包含任何数据.比如jpg图片或者序列化的对象.从内部实现来看其实string可以看作byte ...