套接字,TCP,UDP
一、Socket
1、socket由来
#Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。 #我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
2、socket分类
#1、基于文件类型的套接字家族#套接字家族的名字:AF_UNIX unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信#2、基于网络类型的套接字家族套接字家族的名字:AF_INET
常用的TCP/IP协议的3种套接字类型如下所示。#流式套接字(SOCK_STREAM): TCP 流式套接字用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送,并按顺序接收。流式套接字之所以能够实现可靠的数据服务,原因在于其使用了传输控制协议,即TCP(The Transmission Control Protocol)协议。#数据报套接字(SOCK_DGRAM): 数据报套接字提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP(User Datagram Protocol)协议进行数据的传输。由于数据报套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在程序中做相应的处理。#原始套接字(SOCK_RAW):原始套接字(SOCKET_RAW)允许对较低层次的协议直接访问,比如IP、 ICMP协议,它常用于检验新的协议实现,或者访问现有服务中配置的新设备,因为RAW SOCKET可以自如地控制Windows下的多种协议,能够对网络底层的传输机制进行控制,所以可以应用原始套接字来操纵网络层和传输层应用。比如,我们可以通过RAW SOCKET来接收发向本机的ICMP、IGMP协议包,或者接收TCP/IP栈不能够处理的IP包,也可以用来发送一些自定包头或自定协议的IP包。网络监听技术很大程度上依赖于SOCKET_RAW#原始套接字与标准套接字(标准套接字指的是前面介绍的流式套接字和数据报套接字)的区别在于:原始套接字可以读写内核没有处理的IP数据包,而流式套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送数据必须使用原始套接字。
流式套接字/数据报套接字/原始套接字
3、socket常用方法
#服务端套接字函数 s.bind() 绑定(主机,端口号)到套接字 s.listen() 开始TCP监听 s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来#客户端套接字函数 s.connect() 主动初始化TCP服务器连接 s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常#公共用途的套接字函数 s.recv() 接收TCP数据 s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完) s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完) s.recvfrom() 接收UDP数据 s.sendto() 发送UDP数据 s.getpeername() 连接到当前套接字的远端的地址 s.getsockname() 当前套接字的地址 s.getsockopt() 返回指定套接字的参数 s.setsockopt() 设置指定套接字的参数 s.close() 关闭套接字#面向锁的套接字方法 s.setblocking() 设置套接字的阻塞与非阻塞模式 s.settimeout() 设置阻塞套接字操作的超时时间 s.gettimeout() 得到阻塞套接字操作的超时时间#面向文件的套接字的函数 s.fileno() 套接字的文件描述符 s.makefile() 创建一个与该套接字相关的文件
View Code
二、基于TCP协议的套接字
1、TCP简介
#是什么传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议
2、三次握手
刚开始, 客户端和服务器都处于 CLOSE 状态. 此时, 客户端向服务器主动发出连接请求, 服务器被动接受连接请求.1, TCP服务器进程先创建传输控制块TCB, 时刻准备接受客户端进程的连接请求, 此时服务器就进入了 LISTEN(监听)状态 2, TCP客户端进程也是先创建传输控制块TCB, 然后向服务器发出连接请求报文,此时报文首部中的同步标志位SYN=1, 同时选择一个初始序列号 seq = x, 此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定, SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。 3, TCP服务器收到请求报文后, 如果同意连接, 则发出确认报文。确认报文中的 ACK=1, SYN=1, 确认序号是 x+1, 同时也要为自己初始化一个序列号 seq = y, 此时, TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据, 但是同样要消耗一个序号。 4, TCP客户端进程收到确认后还, 要向服务器给出确认。确认报文的ACK=1,确认序号是 y+1,自己的序列号是 x+1. 5, 此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。
View Code
如图:(TCP是基于链接的,必须先启动服务端,然后再启动客户端去连接服务端)
3、四次挥手
数据传输完毕后,双方都可以释放连接. 此时客户端和服务器都是处于ESTABLISHED状态,然后客户端主动断开连接,服务器被动断开连接.1, 客户端进程发出连接释放报文,并且停止发送数据。 释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。 2, 服务器收到连接释放报文,发出确认报文,ACK=1,确认序号为 u+1,并且带上自己的序列号seq=v,此时服务端就进入了CLOSE-WAIT(关闭等待)状态。 TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。 3, 客户端收到服务器的确认请求后,此时客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最终数据) 4, 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,确认序号为v+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。5, 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,确认序号为w+1,而自己的序列号是u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。 6, 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
View Code
如图
上图中 为什么客户端还要等s=2SML
MSL(Maximum Segment Lifetime),TCP允许不同的实现可以设置不同的MSL值。第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。
View Code
4、如果已经建立了连接,客户端突发故障了
TCP设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
View Code
5、代码实现
import socket ip_port=('127.0.0.1',8081)#电话卡 BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 s.bind(ip_port) #手机插卡 s.listen(5) #手机待机while True: #新增接收链接循环,可以不停的接电话conn,addr=s.accept() #手机接电话print('接到来自%s的电话' %addr[0])while True: #新增通信循环,可以不断的通信,收发消息msg=conn.recv(BUFSIZE) #听消息,听话,接收数据大小# if len(msg) == 0:break #如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生 conn.send(msg.upper()) #发消息,说话 conn.close() #挂电话 s.close() #手机关机
服务端
import socket ip_port=('127.0.0.1',8081) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect_ex(ip_port) #拨电话while True: #新增通信循环,客户端可以不断发收消息msg=input('>>: ').strip()if len(msg) == 0:continues.send(msg.encode('utf-8')) #发消息,说话(只能发送字节类型) feedback=s.recv(BUFSIZE) #收消息,听话print(feedback.decode('utf-8'))s.close() #挂电话
客户端
三、基于UDP的套接字
1、UDP简介
#是什么 用户数据报协议 #UDP是一个无连接协议,把数据报发送出去,但并不保证它们能够到达目的地它是无连接的,先启动哪一端都可以
2、TCP与UDP的区别
3、简单代码示例
import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)udp_server_client.bind(ip_port)while True:msg,addr=udp_server_client.recvfrom(BUFSIZE)print(msg,addr)udp_server_client.sendto(msg.upper(),addr)
服务端
import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)while True:msg=input('>>: ').strip()if not msg:continueudp_server_client.sendto(msg.encode('utf-8'),ip_port)back_msg,addr=udp_server_client.recvfrom(BUFSIZE)print(back_msg.decode('utf-8'),addr)
客户端
4、模拟qq聊天(UDP是无连接的,可以同时多个客户端跟服务端通信)
import socket ip_port=('127.0.0.1',8081) udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #买手机 udp_server_sock.bind(ip_port)while True:qq_msg,addr=udp_server_sock.recvfrom(1024)print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))back_msg=input('回复消息: ').strip()udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
服务端
import socket BUFSIZE=1024 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)qq_name_dic={'c1':('127.0.0.1',8081),'c2':('127.0.0.1',8081),'c3':('127.0.0.1',8081),'c4':('127.0.0.1',8081), }while True:qq_name=input('请选择聊天对象: ').strip()while True:msg=input('请输入消息,回车发送: ').strip()if msg == 'quit':breakif not msg or not qq_name or qq_name not in qq_name_dic:continueudp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))udp_client_socket.close()
客户端1
import socket BUFSIZE=1024 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)qq_name_dic={'c1':('127.0.0.1',8081),'c2':('127.0.0.1',8081),'c3':('127.0.0.1',8081),'c4':('127.0.0.1',8081), }while True:qq_name=input('请选择聊天对象: ').strip()while True:msg=input('请输入消息,回车发送: ').strip()if msg == 'quit':breakif not msg or not qq_name or qq_name not in qq_name_dic:continueudp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))udp_client_socket.close()
客户端2
四、粘包
1、粘包简介
#只有TCP有粘包现象,UDP永远不会粘包,所谓粘包,主要问题是接收方不知道消息的边界,不知道一次性取多少字节造成的
2、发生粘包的两种情况
#发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)#接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
3、如何解决粘包
#1、先发送长度信息,再发送真实信息#2、struct模块
import socket,struct,json import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加 phone.bind(('127.0.0.1',8080))phone.listen(5)while True:conn,addr=phone.accept()while True:cmd=conn.recv(1024)if not cmd:breakprint('cmd: %s' %cmd)res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)err=res.stderr.read()print(err)if err:back_msg=errelse:back_msg=res.stdout.read()conn.send(struct.pack('i',len(back_msg))) #先发back_msg的长度conn.sendall(back_msg) #在发真实的内容 conn.close()
服务端(自定制报头)
import socket,time,structs=socket.socket(socket.AF_INET,socket.SOCK_STREAM) res=s.connect_ex(('127.0.0.1',8080))while True:msg=input('>>: ').strip()if len(msg) == 0:continueif msg == 'quit':breaks.send(msg.encode('utf-8'))l=s.recv(4)x=struct.unpack('i',l)[0]print(type(x),x)# print(struct.unpack('I',l))r_s=0data=b''while r_s < x:r_d=s.recv(1024)data+=r_dr_s+=len(r_d)# print(data.decode('utf-8'))print(data.decode('gbk')) #windows默认gbk编码
客户端(自定制报头)
import socket,struct s = socket.socket() s.bind(("127.0.0.1",8888)) s.listen(5) conn,addr = s.accept() c_len_data = conn.recv(4) c_len = struct.unpack("i",c_len_data) print(c_len) #执行后 >>(28,) 输出的是元组类型,需要取第一个 客户端 import socket,json,struct c = socket.socket() c.connect(("127.0.0.1",8888)) c_dic = {"name":"pdun","age":"1"} c_data = json.dumps(c_dic).encode("utf-8") c_len = struct.pack("i",len(c_data)) c.send(c_len) c.send(c_data)
struct
转载于:https://www.cnblogs.com/pdun/p/11287656.html
套接字,TCP,UDP相关推荐
- socket网络编程套接字TCP/UDP两种方式详解
目录 准备知识 源IP地址和目的IP地址 端口号与进程ID 传输层协议--TCP 传输层协议--UCP 网络字节序 socket套接字介绍 概念 常见的三种socket socket编程常见API s ...
- RAW 原始套接字 TCP UDP协议解析
前言 公司的项目终于告一段落了,终于有时间了 - - leetcode 终于又可以 提上日程了 rust 驱动开发,也可以继续做了 kernel的驱动开发,也得继续进行了 驱动框架我又回来了 RFC ...
- Python网络编程——socket套接字实现UDP/TCP信息传输
socket套接字 socket(简称 套接字) ,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来 ...
- 【JavaEE】网络编程之TCP套接字、UDP套接字
目录 1.网络编程的基本概念 1.1为什么需要网络编程 1.2服务端与用户端 1.3网络编程五元组 1.4套接字的概念 2.UDP套接字编程 2.1UDP套接字的特点 2.2UDP套接字API 2.2 ...
- 【网络编程】Socket套接字;UDP数据报套接字编程;TCP流套接字编程
文章目录 1. 什么是网络编程 2. 网络编程中的基本概念 3. Socket套接字 4 UDP数据报套接字编程 4.1 客户端服务器交互流程 4.2 UDP版本的回显服务 4.3 英译汉服务 5. ...
- python--socket套接字/TCP
socket套接字/TCP 一 客户端/服务器架构 C/S架构,包括 硬件C/S架构(打印机) 软件C/S 架构(web服务) C/S架构的软件(软件属于应用层)是基于网络进行通信的 Server端要 ...
- socket套接字TCP API
socket套接字TCP API socket概念 socket又称"套接字",是计算机网络中进程间通信数据通道的一个端点,或称之为句柄.IP地址+端口号就可以唯一确定一个sock ...
- 套接字技术java_java网络编程之套接字TCP
套接字学习 什么是套接字? 计算机网络基础知识 基于套接字的网路编程案例? 什么是套接字? 所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.应用程 ...
- Linux C 套接字socket UDP
server端: int sockfd; sockfd = socket(AF_INET, SOCK_DGRAM, 0); //创建套接字 SOCK_DGRAM : UDP s ...
- 套接字TCP控制台程序客户端代码示范
套接字TCP控制台程序客户端代码示范 转载于:https://www.cnblogs.com/txwtech/p/11056770.html
最新文章
- 浏览器用xpath获取一直为空
- cvCalcBackProject的例子
- python绘制3d动态模型_怎么用python把*.obj文件里面的3D模型特征提取出来?
- 同时获取同一等级下多个class值的节点的方法
- Oracle入门(三A)之sqlplus
- 2017年计算机三级网络技术试题,2017年计算机三级网络技术考前试题及答案(8)
- php可以用水晶报表吗,什么是水晶报表与水晶报表功能分析
- 【华为云技术分享】从 Cloud 1.0 到 2.0,云计算的“多元架构命题”
- 让一个元素水平垂直居中的方法
- WordPress学习笔记(三)其他功能
- 在ourdev上看的一个帖子
- 写文章不会起标题?爬取虎嗅5万篇文章告诉你
- android 11.0 12.0去掉前置摄像头闪光灯功能
- 全网最详细官网一键换肤教程
- scala with cats 之 Contravariant Functors and Invariant Functors
- oppo A37全网通刷机包原厂售后线刷包自带工具
- Windows下安装VMware
- JAVA判断当前日期是否是工作日,还是节假日
- 微信订阅号通过获取Openid并获取用户基本信息
- 论文翻译 《Self-supervised Learning of LiDAR Odometry for Robotic Applications》