1、tcp相关介绍

TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。

TCP通信需要经过创建连接数据传送终止连接三个步骤。

TCP通信模型中,在通信开始之前, 一定要先建立相关的链接, 才能发送数据, 类似于生活中, "打电话""

TCP特点

  1. 面向连接

通信双方必须先建立连接才能进行数据的传输,双方都必须为该连接分配必要的系统内核资源,以管理连接的状态和连接上的传输。

双方间的数据传输都可以通过这一个连接进行。

完成数据交换后,双方必须断开此连接,以释放系统资源。

这种连接是一对一的,因此TCP不适用于广播的应用程序,基于广播的应用程序请使用UDP协议。

  1. 可靠传输

    1. TCP采用发送应答机制

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

    1. 超时重传

    发送端发出一个报文段之后就启动定时器,如果在定时时间内没有收到应答就重新发送这个报文段。

    TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。

    1. 错误校验

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

    1. 流量控制和阻塞管理

    流量控制用来避免主机发送得过快而使接收方来不及完全收下。

TCP与UDP的不同点

  • 面向连接(确认有创建三方交握,连接已创建才作传输。)
  • 有序数据传输
  • 重发丢失的数据包
  • 舍弃重复的数据包
  • 无差错的数据传输
  • 阻塞/流量控制

2、TCP服务器

实际生活中:

  1. 买个手机
  2. 插上手机卡
  3. 设计手机为正常接听状态(即能够响铃)
  4. 静静的等着别人拨打

在程序中, 如果想要完成一个TCP服务器的功能, 需要的流程如下:

  1. socket创建一个套接字
  2. bind绑定ip和port
  3. listen使套接字变为可以被动链接
  4. accept等待客户端的链接
  5. recv/send接收发送数据

一个很简单的TCP服务器如下:

  1. from socket import *
  2. # 1.创建一个socket套接字(买手机卡)
  3. tcpSerSocket = socket(AF_INET, SOCK_STREAM)
  4. # 2.bind绑定本机ip和port(插上手机卡)
  5. address = (''. 7777)
  6. tcpSerSocket.bind(address)
  7. # 3.listen使套接字变为可以被动链接(设置为手机正常接听状态)
  8. tcpSerSocket.listen(5)
  9. # 4.accept等待客户端的链接(静静的等着别人拨打)
  10. # newSocket用来为这个客户端服务
  11. # tcpSerSocket就可以省下来专门等待其他新客户端的链接
  12. newSocket, clientAddr = tcpSerSocket.accept()
  13. # 5.recv接收数据,最大接收1024个字节
  14. recvData = newSocket.recv(1024).decode('gbk')
  15. print('接收到的数据为:',recvData)
  16. # 6.send发送一些数据到客户端
  17. newSocket.send("服务器已经收到了!".encode('gbk'))
  18. # 关闭为这个客户端服务的套接字, 只要关闭了, 就意味着为不能再为这个客户端服务
  19. newSocket.close()
  20. # 关闭监听套接字, 只要这个套接字关闭了, 就意味着整个程序不能再接收任何新的客户端的连接
  21. tcpSerSocket.close()

3、TCP客户器

TCP的客户端要比服务器端简单很多,如果说服务器端是需要自己买手机、查手机卡、设置铃声、等待别人打电话流程的话,那么客户端就只需要找一个电话亭,拿起电话拨打即可,流程要少很多

生活中的电话机,如果想让别人能更够打通咱们的电话获取相应服务的话,需要做以下几件事情:

  1. 买个手机
  2. 插上手机卡
  3. 设计手机为正常接听状态(即能够响铃)
  4. 静静的等着别人拨打
  1. from socket import *
  2. from time import *
  3. # 1.socket创建一个套接字
  4. tcpClient = socket(AF_INET,SOCK_STREAM)
  5. # 2.绑定本机端口号,也可以不绑定
  6. tcpClient.bind(('',8888))
  7. # 3.连接服务器
  8. tcpClient.connect(('192.168.14.72',5678))
  9. #4.接收消息
  10. data1 = tcpClient.recv(1024)
  11. print(data1.decode('gbk'))
  12. #5.发消息
  13. while True:
  14. data = input('>>\t').encode('gbk')
  15. tcpClient.send(data)
  16. if data=='886':
  17. sleep(4)
  18. tcpClient.close()
  19. break

4.应用:模拟QQ聊天

客户端参考代码

  1. from socket import *
  2. # 创建socket
  3. tcpClientSocket = socket(AF_INET, SOCK_STREAM)
  4. # 链接服务器
  5. serAddr = ('192.168.1.102', 7788)
  6. tcpClientSocket.connect(serAddr)
  7. while True:
  8. # 提示用户输入数据
  9. sendData = input("send:")
  10. if len(sendData)>0:
  11. tcpClientSocket.send(sendData)
  12. else:
  13. break
  14. # 接收对方发送过来的数据,最大接收1024个字节
  15. recvData = tcpClientSocket.recv(1024)
  16. Print('recv:',recvData)
  17. # 关闭套接字
  18. tcpClientSocket.close()

服务器端参考代码

  1. #coding=utf-8
  2. from socket import *
  3. # 创建socket
  4. tcpSerSocket = socket(AF_INET, SOCK_STREAM)
  5. # 绑定本地信息
  6. address = ('', 7788)
  7. tcpSerSocket.bind(address)
  8. # 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了
  9. tcpSerSocket.listen(5)
  10. while True:
  11. # 如果有新的客户端来链接服务器,那么就产生一个信心的套接字专门为这个客户端服务器
  12. # newSocket用来为这个客户端服务
  13. # tcpSerSocket就可以省下来专门等待其他新客户端的链接
  14. newSocket, clientAddr = tcpSerSocket.accept()
  15. while True:
  16. # 接收对方发送过来的数据,最大接收1024个字节
  17. recvData = newSocket.recv(1024)
  18. # 如果接收的数据的长度为0,则意味着客户端关闭了链接
  19. if len(recvData)>0:
  20. print 'recv:',recvData
  21. else:
  22. break
  23. # 发送一些数据到客户端
  24. sendData = input("send:")
  25. newSocket.send(sendData)
  26. # 关闭为这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接
  27. newSocket.close()
  28. # 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接
  29. tcpSerSocket.close()

5.应用:tcp通信-多线程服务器端

  1. from socket import *
  2. from threading import *
  3. class Mythread(Thread):
  4. def __init__(self, socketClient, clientAddress):
  5. Thread.__init__(self)
  6. self.socketClient = socketClient
  7. self.clientAddress = clientAddress
  8. def run(self):
  9. data1 = '哈哈。。。可以聊天了'.encode('utf_8')
  10. self.socketClient.send(data1)
  11. while True:
  12. try:
  13. # 接收客户端发的消息
  14. data = self.socketClient.recv(1024).decode('utf-8')
  15. except:
  16. print('<<\t%s-%s 已经断开了' % (self.clientAddrdess[0], self.clientAddrdess[1]))
  17. break
  18. if data == '886':
  19. break
  20. print('<<\t%s-%s:%s' % (self.clientAddrdess[0], self.clientAddrdess[1], data))
  21. def main():
  22. tcpServer = socket(AF_INET, SOCK_STREAM)
  23. tcpServer.bind(('', 5678))
  24. tcpServer.listen(5)
  25. while True:
  26. print('服务器等待客户端连接......')
  27. socketClient, clientAddress = tcpServer.accept()
  28. Mythread(socketClient, clientAddress).start()
  29. print('%s-%s 连接成功......' % (clientAddrdess[0], clientAddrdess[1]))
  30. if __name__ == '__main__':
  31. main()
  32. #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短连接的情况:

  1. client 向 server 发起连接请求
  2. server 接到请求,双方建立连接
  3. client 向 server 发送消息
  4. server 回应 client
  5. 一次读写完成,此时双方任何一个都可以发起 close 操作

在第 步骤5中,一般都是 client 先发起 close 操作。当然也不排除有特殊的情况。

从上面的描述看,短连接一般只会在 client/server 间传递一次读写操作!

9.2TCP长连接

再模拟一种长连接的情况:

  1. client 向 server 发起连接
  2. server 接到请求,双方建立连接
  3. client 向 server 发送消息
  4. server 回应 client
  5. 一次读写完成,连接不关闭
  6. 后续读写操作...
  7. 长时间操作之后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的更多相关文章

  1. C#网络编程之---TCP协议的同步通信(二)

    上一篇学习日记C#网络编程之--TCP协议(一)中以服务端接受客户端的请求连接结尾既然服务端已经与客户端建立了连接,那么沟通通道已经打通,载满数据的小火车就可以彼此传送和接收了.现在让我们来看看数据的 ...

  2. 嵌入式linux的网络编程(1)--TCP/IP协议概述

    嵌入式linux的网络编程(1)--TCP/IP协议概述 1.OSI参考模型及TCP/IP参考模型 通信协议用于协调不同网络设备之间的信息交换,它们建立了设备之间互相识别的信息机制.大家一定都听说过著 ...

  3. 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系

    [Linux网络编程]TCP网络编程中connect().listen()和accept()三者之间的关系 基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: conn ...

  4. 【网络编程1】网络编程基础-TCP、UDP编程

    网络基础知识 网络模型知识 OSI七层模型:(Open Systems Interconnection Reference Model)开放式通信系统互联参考模型,是国际标准化组织(ISO)提出的一个 ...

  5. 【转载】[基础知识]【网络编程】TCP/IP

    转自http://mc.dfrobot.com.cn/forum.php?mod=viewthread&tid=27043 [基础知识][网络编程]TCP/IP iooops  胖友们楼主我又 ...

  6. Java 网络编程 -- 基于TCP 模拟多用户登录

    Java TCP的基本操作参考前一篇:Java 网络编程 – 基于TCP实现文件上传 实现多用户操作之前先实现以下单用户操作,假设目前有一个用户: 账号:zs 密码:123 服务端: public c ...

  7. python网络编程05 /TCP阻塞机制

    python网络编程05 /TCP阻塞机制 目录 python网络编程05 /TCP阻塞机制 1.什么是拥塞控制 2.拥塞控制要考虑的因素 3.拥塞控制的方法: 1.慢开始和拥塞避免 2.快重传和快恢 ...

  8. 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程

    Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...

  9. C#网络编程之--TCP协议(一)

    TCP 是面向连接的传输协议 面向连接,其实就好比,A打电话给B,如果B接听了,那么A和B之间就的通话,就是面向连接的 TCP 是全双工的传输协议 全双工,这个理解起来也很简单,A打电话给B,B接听电 ...

随机推荐

  1. hdu 1050 Moving Tables(迷之贪心...)

    题意:有400间房间按题目中图片所给的方式排列,然后给出要移动的n张桌子所要移动的范围,每张桌子要移动的范围不能出现重叠的区域:问最少要多少次才能移动完所有的桌子. 题解思路:把题目转换下,就是有n个 ...

  2. mac Homebrew Updating慢,替换及重置Homebrew默认源

    替换成清华的镜像: https://lug.ustc.edu.cn/wiki/mirrors/help/brew.git

  3. oc58--Category注意事项

    // // main.m // Category注意事项 #import <Foundation/Foundation.h> #import "Person+NJ.h" ...

  4. C# winform窗体在桌面右下角显示(任务栏上方)

    问题描述: 有一个主窗口程序,需要给该程序添加一个通知子窗口.子窗口的位置为右下角. 解决方法: 在子窗口frmPopMsg的代码文件中添加如下代码: public frmPopMsg() { Ini ...

  5. 深入浅出时序数据库之预处理篇——批处理和流处理,用户可定制,但目前流行influxdb没有做

    时序数据是一个写多读少的场景,对时序数据库以及数据存储方面做了论述,数据查询和聚合运算同样是时序数据库必不可少的功能之一.如何支持在秒级对上亿数据的查询分组聚合运算成为了时序数据库产品必须要面对的挑战 ...

  6. CSS--浏览器CSS Hack 收集

    所谓的Hack就是只有特定浏览器才能识别这段hack代码.Hack 不是什么好东西,除非没有办法,我们尽量还是不要用着玩意. 下面是各个浏览器的CSS Hack 列表. Firefox 浏览器 @-m ...

  7. sublime的ctags安装

    首先,是ctags的下载.在这里:http://pan.baidu.com/s/1gdAMFab 我们用sublime几乎都会首先安装这个插件,这个插件是管理插件的功能,先安装它,再安装其他插件就方便 ...

  8. BZOJ 1037 生日聚会 DP

    [ZJOI2008]生日聚会Party Time Limit: 10 Sec Memory Limit: 162 MB Description 今天是hidadz小朋友的生日,她邀请了许多朋友来参加她 ...

  9. F - Micro-World(简单模拟)

    Problem description You have a Petri dish with bacteria and you are preparing to dive into the harsh ...

  10. ACM_魔仙岛探险(深搜)

    魔仙岛探险 Time Limit: 2000/1000ms (Java/Others) Problem Description: 小敏通过秘密方法得到一张不完整的魔仙岛航拍地图.魔仙岛由一个主岛和一些 ...