socket编程(代码片段)

roygood roygood     2023-01-07     241

关键词:

TCP下粘包问题

两种情况下会发生粘包。

1、发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

发送方:AB  #其实放在缓存里没发送

发送方:B  #其实放在缓存里没发送

发送方:CD  #缓存满了,发一波

接收方:ABBCD  #及时从缓存里接收信息,我擦,发这是啥答案?

两同学传答案因粘包发生误会,后果严重

2、接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 

发送方:211  #其实放在缓存里没发送

发送方:12  #其实放在缓存里没发送

发送方:985  #缓存满了,发一波

接收方:21112985  #没有及时从缓存里接收信息,我擦第一题结果这么大?

两同学传答案因粘包发生误会,后果严重

拆包的发生情况

当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。

为何tcp是可靠传输,udp是不可靠传输

tcp在数据传输时,发送端先把数据发送到自己的缓存中,然后协议控制将缓存中的数据发往对端,对端返回一个ack=1,发送端则清理缓存中的数据,对端返回ack=0,则重新发送数据,所以tcp是可靠的

而udp发送数据,对端是不会返回确认信息的,因此不可靠

send(字节流)和recv(1024)及sendall

recv里指定的1024意思是从缓存里一次拿出1024个字节的数据

send的字节流是先放入己端缓存,然后由协议控制将缓存内容发往对端,如果待发送的字节流大小大于缓存剩余空间,那么数据丢失,用sendall就会循环调用send,数据不会丢失。

粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

  1. TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
  2. UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
  3. tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头。

udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成,若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠

tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。

 

为了解决粘包问题我们可以考虑发送消息时同时发送关于消息的长度信息,接收方安长度信息提取消息

发送方:(3)211  #其实放在缓存里没发送

发送方:(2)12   #其实放在缓存里没发送

发送方:(3)985  #缓存满了,发一波

接收方:(3)211(2)12(3)985  #没有及时从缓存里接收信息,但是收到了长度信息不用方,按长度信息读取得答案:211  12  985

下面是一个解决粘包的实例

服务端

 1 #!/usr/bin/python
 2 # -*- coding: utf-8 -*-
 3 """
 4 基于TCP实现远程执行命令,发送数据长度信息解决粘包问题,这是服务端
 5 """
 6 import socket,json,struct
 7 import subprocess
 8 
 9 ip_port=(‘服务端IP,9000)
10 back_log=5
11 buffer_size=1024
12 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
13 
14 #setsockopt解决重启服务端服务端仍然存在四次挥手的time_wait状态在占用地址
15 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #加入一条socket配置,重用ip和端口,
16 
17 phone.bind(ip_port)#绑定(主机,端口号)到套接字
18 
19 phone.listen(back_log)#开始监听
20 
21 while True:                   #连接循环
22     conn,addr=phone.accept()
23     while True:               #通信循环
24         cmd=conn.recv(buffer_size)   #接收消息,recv里指定的1024意思是从缓存里一次拿出1024个字节的数据
25         if not cmd:break      #cmd为空跳出循环
26         print(cmd:%s %cmd)
27         res=subprocess.Popen(cmd.decode(utf-8),
28                              shell=True,
29                              stdout=subprocess.PIPE,
30                              stderr=subprocess.PIPE
31                              )#此函数将解码后的cmd给shell去解释,stdout输出参数,stderr报错参数
32         err=res.stderr.read() #读出报错
33         print(err)
34         if err:
35             back_msg=err
36         else:
37             back_msg=res.stdout.read()
38         #发送
39         headers=data_size:len(back_msg) #包含数据长度信息的报头
40         head_json=json.dumps(headers) #将报头序列化用于传输
41         head_json_bytes=bytes(head_json,encoding=utf-8) #再字节化
42 
43         #为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
44         conn.send(struct.pack(i,len(head_json_bytes))) #先发报头长度,这4个字节里只包含了一个数字,该数字是报头的长度
45         conn.send(head_json_bytes) #再发报头
46         conn.sendall(back_msg) #再发真实内容
47         #s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
48      #s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
49     conn.close() #关闭套接字

客户端

 1 #!/usr/bin/python
 2 # -*- coding: utf-8 -*-
 3 """
 4 基于TCP实现远程执行命令,发送数据长度信息解决粘包问题,这是客户端
 5 """
 6 import socket,json,struct
 7 
 8 ip_port=(‘服务端IP,9000)
 9 back_log=5
10 buffer_size=1024
11 client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
12 
13 #s.connect()     主动初始化TCP服务器连接
14 #s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
15 client.connect_ex(ip_port)
16 
17 while True:
18     cmd=input(>>: )
19     if not cmd:continue #防止输入空值
20     if cmd == quit:break
21     client.send(bytes(cmd,encoding=utf-8)) #将命令编码后转为字节发送
22 
23     head=client.recv(4) #接收长度为4个字节的报头长度信息
24     head_json_len=struct.unpack(i,head)[0] #将报头长度信息解包,得到报头长度
25     head_json=json.loads(client.recv(head_json_len).decode(utf-8))
26     #利用报头长度取出报头并解码、反序列化,得到报头
27     data_len=head_json[data_size] #从报头里取出数据长度
28 
29     recv_size=0
30     recv_data=b‘‘
31     while recv_size < data_len:
32         recv_data += client.recv(1024) #一次跨1024字节收数据
33         recv_size += len(recv_data) #计算已得到数据长度
34 
35     #print(recv_data.decode(‘utf-8‘))
36     print(recv_data.decode(gbk)) #windows默认gbk编码

 以上实现了客户端与服务器的连接并解决了粘包问题,但是不能实现并发,服务器端只能一对一服务,不能一对多服务

为了实现并发我们引入socketserver,以下代码只针对实现并发

并发服务端

 1 #!/usr/bin/python
 2 # -*- coding: utf-8 -*-
 3 
 4 import socketserver
 5 
 6 class MyServer(socketserver.BaseRequestHandler):
 7     def handle(self):
 8         print(self.request) #conn
 9         print(self.client_address) #addr
10 
11         while True:
12             try:
13                 #收消息
14                 data=self.request.recv(1024)
15                 print("收到消息",data)
16                 #发消息
17                 self.request.sendall(data.upper())
18             except Exception as e:
19                 print(e)
20                 break
21 if __name__ == __main__:
22     s=socketserver.ThreadingTCPServer((192.168.1.106,9000),MyServer)
23     s.serve_forever()

客户端

 1 #!/usr/bin/python
 2 # -*- coding: utf-8 -*-
 3 import socket,json,struct
 4 
 5 ip_port=(192.168.1.106,9000)
 6 back_log=5
 7 buffer_size=1024
 8 client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 9 
10 #s.connect()     主动初始化TCP服务器连接
11 #s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
12 client.connect_ex(ip_port)
13 
14 while True:
15     cmd=input(>>: )
16     if not cmd:continue #防止输入空值
17     if cmd == quit:break
18     client.send(bytes(cmd,encoding=utf-8)) #将命令编码后转为字节发送
19 
20     data=client.recv(buffer_size)
21     print(收到服务端发来的消息,data.decode(utf-8))
22 
23 client.close()

此时可用多个客户端与服务器交互

 

socket编程(代码片段)

socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。下面来看看一个简单的通信过程 服务端:importsocketphone=socket.socket(socket.AF_INET,socket.SOCK_S... 查看详情

socket编程(代码片段)

文章目录Socket编程图示0.相关函数WSADATA说明WSAStartupsocket()socketaddr和socketaddr_inin_addrinet_addrhtonsconnect()recv()send()closesocket()WSACleanup()bind()listen()accept()1.VS2019遇到的一些问题a.函数过期b.两个项目2.代码a.服务器端b.客 查看详情

socket编程开发(代码片段)

1.socket是什么?Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组... 查看详情

网络编程:套接字socket(代码片段)

socket是基于C/S架构的,也就是说进行socket网络编程,通常需要编写两个py文件,一个服务端,一个客户端。首先,导入Python中的socket模块:importsocket其通信逻辑如下图所示:这张图片是整个socket编程的基础,必须牢牢记住。通过... 查看详情

chapter12.2socket编程的udp编程(代码片段)

socket的UDP编程和socketserver模块 UDP编程 UDP服务端流程UDP服务端创建socket对象。socket.SOCK_DGRAM绑定IP和Port,bind()方法传输数据接收数据,socket.recvfrom(bufsize[,flags]),获得一个二元组(string,address)发送数据,socket.sendto(string,addre 查看详情

linux网络编程---socket编程(代码片段)

1.网络套接字函数socket模型创建流程图1.1socket函数#include<sys/types.h>/*SeeNOTES*/#include<sys/socket.h>intsocket(intdomain,inttype,intprotocol);domain: AF_INET这是大多数用来产生socket的协议,使用TCP或UDP来传输࿰ 查看详情

网络编程--socket(代码片段)

单机测试:服务端:publicclassSynchronousSocketListener//Incomingdatafromtheclient.publicstaticstringdata=null;publicstaticvoidStartListening()//Databufferforincomingdata.byte[]bytes=newByte[1024];//Establish 查看详情

socket编程初识(代码片段)

一、socket1、socket层2、socket的理解写python代码的时候socket就像是一个模块,通过import导入,通过调用模块中的方法建立两个进程之间的连接和通信。Socket是应用层与传输层通信的中间软件抽象层,它是一组接口。它是为了帮我们... 查看详情

socket编程-tcp(代码片段)

server.pyimportsocketphone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#第一个参数为套接字的地址家族AF_INET代表网络套接字,第二个参数SOCK_STREAM代表tcp协议phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#当服务端关闭时,再重启服务端 查看详情

go语言系列-socket编程和redis(代码片段)

Socket编程一、socket编程概述  什么是socket编程?socket编程是计算机PC机器上2个程序通过一个双向的通信连接实现数据的交互,这个连接的一端就是一个socket。socket的翻译意思上还有个插座的概念,其实,也可以很形象的比喻为... 查看详情

02socket网络编程(代码片段)

概述:Socket又称为套接字,用于描述IP地址和端口,是一个通信链的句柄;Socket是为网络编程提供的一种机制;通信两端都有Socket,网络编程就是Socket之间的通信,数据在两个Socket之间通过IO流传输。学习Socket编程之前,先要了解... 查看详情

python的socket编程(代码片段)

  socket主要用于长连接 简单DEMO:Server端:importsocketserver=socket.socket(socket.AF_INET,socket.SOCK_STREAM)server.bind((‘0.0.0.0‘,8000))server.listen()sock,addr=server.accept()#获取从客户端发送的数据data=s 查看详情

基本的socket编程(代码片段)

importjava.net.ServerSocket;importjava.net.Socket;importjava.io.InputStreamReader;importjava.io.OutputStream;importjava.io.IOException;importjava.io.BufferedReader;importjava.io.PrintStream;/**ServerS 查看详情

socket网络编程(代码片段)

windows下的socket网络编程windows下的socket网络编程clinet.c客户端server.c服务器端UDP通信的实现代码如下已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用... 查看详情

socket编程(代码片段)

Socket 概念:是连接运行在网络上的两个程序间的双向通讯的端点  C/S 客户机/服务器   B/S  浏览器/服务器 基于TCP的Socket编程 创建TCP服务端步骤  创建一个ServerSocket对象  调用accept()方法接... 查看详情

socket编程(代码片段)

转自http://blog.csdn.net/gneveek/article/details/8699198“一切皆Socket!”话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket。——有感于实际编程和开源项目研究。我们深谙信息交流的价值,那网络中进程之间如何通信,... 查看详情

socket基础编程-1(代码片段)

server端和client端1、server端:importsocketserver=socket.socket()server.bind((‘localhost‘,8080))server.listen()conn,attr=server.connect()data=conn.recv(10240)re=b‘.....‘conn.send(re)server.close2、client端imp 查看详情

python之网络编程socket编程(代码片段)

本节内容:Socket语法及相关SocketServer实现多并发    Socket语法及相关 socket概念socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递。我们知道网络通信都是基... 查看详情