day8---多线程socket 编程,tcp粘包处理

复习下socket 编程的步骤:

服务端:

1 声明socket 实例

server = socket.socket()  #括号里不写  默认地址簇使用AF_INET  即 IPv4       默认type 为 sock.SOCK_STREAM 即 TCP/IP 协议   

2 绑定IP地址和端口

server.bind((‘localhost‘,9999))  #ip地址和端口  元组形式 ,端口为整数形式

3 开始监听

server.listen()

4 进入阻塞状态,等待连接进入

######## 从这里开始可以加入循环,循环不同的连接 ,这样就可以接受不同的客户端的连接了#####

#while True:

  # 获取客户端ip地址和端口  开启一个连接实例

##############  从这里开始循环,这样就可以循环收到客户端数据,但是只收这一个客户端 ###########

##客户端一断开,conn收到的就全为空数据,这样就进入了一个死循环,所以需要一个if 判断

#while True:

5 收数据

data = conn.recv(1024)  #接收最大数据量, 官方建议最大8192  写整数据

# 这里加入if 判断

# if not data:

#    break

print(data)

6 可以往回发送数据

conn.send(data.upper()) #可以发送任何数据,这里把收到的变为大写

7 server.close()

客户端:

1 声明socket 实例:

client= socket.socket()

2 连接服务端:

client.connect((‘server_ip‘,9999))   #连接服务端的ip及端口 元组形式 ,端口为整数形式

3 发数据

client.send(data)   #发送数据,任意数据

# 4 收取服务端返回的数据

#   recv_data = client.recv(1024)

动态导入模块:

 1 实践:
 2 1 lib下的aa
 3 def test():
 4     print("hehe")
 5 class C(object):
 6     name = "yang"
 7     def __init__(self):
 8         self.name = ‘shenyang‘

 9 lib外的test
10 import importlib
11 dd = importlib.import_module(‘lib.aa‘)
12 dd.test()
13 print(dd.C().name)

运行:

断言:

判断类型

正确判断

判断错误

和 if  判断  一样 但是更简洁,一句话就OK

继续socket 编程:

客户端虽然写的是收1024  但是不一定收到1024  只代表最多收到1024  即使服务端返回100个字节 也收

客户端收完数据,收多少 要服务端先发一个要发多少的数据,客户端收到后,开始接收数据,直到收到指定大小的数据为止,不再接收

实践:

服务端

 1 #!/usr/bin/env python3
 2 # Author: Shen Yang
 3 import socket,os
 4 ip_address = ‘192.168.16.10‘
 5 port = 8888
 6 bind_address = (ip_address,port)
 7 server = socket.socket()
 8 server.bind(bind_address)
 9 server.listen()
10 while True:
11     conn,addr = server.accept()
12     while True:
13         data = conn.recv(1024).decode()
14         if not data:
15             print("丢失连接")
16             break
17         print("这是来自",addr,data)
18         cmd_res = os.popen(data).read()
19         conn.send(str(len(cmd_res.encode(‘utf-8‘))).encode(‘utf-8‘))
20         conn.send(cmd_res.encode(‘utf-8‘))
21 server.close()

客户端

 1 #!/usr/bin/env python3
 2 # Author: Shen Yang
 3 import socket
 4 ip_address = ‘192.168.16.10‘
 5 # ip_address = ‘192.168.81.133‘
 6 port = 8888
 7 conn_address = (ip_address,port)
 8 client = socket.socket()
 9 client.connect(conn_address)
10 while True:
11     cmd = input(":> ").encode(‘utf-8‘)
12     if len(cmd) == 0:
13         continue
14     client.send(cmd)
15     cmd_size = int(client.recv(1024).decode())
16     print(cmd_size)
17     recov_size = 0
18     recov_data = b‘‘
19     while recov_size < cmd_size:
20         data = client.recv(1024)
21         #print(data)
22         recov_size += len(data)
23         #print(recov_size)
24         recov_data += data
25     else:
26         print("收完了,大小",recov_size)
27         print(recov_data.decode())
28 client.close()

上面的代码 在linux 发送 会出现粘包   客户端收到的是两个数据

终极解决办法:

服务端添加接收过程:

客户端加发送过程:

实践:

ssh_server:

#!/usr/bin/env python3
import socket,os
ip_address = ‘192.168.81.133‘
port = 8888
bind_address = (ip_address,port)
server = socket.socket()
server.bind(bind_address)
server.listen()
while True:
    conn,addr = server.accept()
    while True:
        data = conn.recv(1024).decode()
        if not data:
            print("丢失连接")
            break
        print("这是来自",addr,data)
        cmd_res = os.popen(data).read()
        conn.send(str(len(cmd_res.encode(‘utf-8‘))).encode(‘utf-8‘))
        ack = conn.recv(1024).decode()
        print(ack)
        conn.send(cmd_res.encode(‘utf-8‘))
server.close()

ssh_client

 1 #!/usr/bin/env python3
 2 import socket
 3 ip_address = ‘192.168.81.133‘
 4 port = 8888
 5 conn_address = (ip_address,port)
 6 client = socket.socket()
 7 client.connect(conn_address)
 8 while True:
 9     cmd = input(":> ").encode(‘utf-8‘)
10     if len(cmd) == 0:
11         continue
12     client.send(cmd)
13     cmd_size = int(client.recv(1024).decode())
14     print(cmd_size)
15     client.send("收到大小".encode(‘utf-8‘))
16     recov_size = 0
17     recov_data = b‘‘
18     while recov_size < cmd_size:
19         data = client.recv(1024)
20         #print(data)
21         recov_size += len(data)
22         #print(recov_size)
23         recov_data += data
24     else:
25         print("收完了,大小",recov_size)
26         print(recov_data.decode())
27 client.close()

传文件:

server 端逻辑:

使用 os.stat  获取文件详细信息,发送给客户端

另外一个解决粘包的方法:

只收指定大小的数据  永远不可能粘包

实践:

ftp_server.py

 1 #!/usr/bin/env python3
 2 import socket,os,hashlib
 3 ip_address = ‘192.168.81.133‘
 4 port = 8888
 5 bind_address = (ip_address,port)
 6 server = socket.socket()
 7 server.bind(bind_address)
 8 server.listen()
 9 while True:
10     conn,addr = server.accept()
11     while True:
12         print("等待新链接")
13         data = conn.recv(1024)
14         if not data:
15             print("丢失连接")
16             break
17         print("这是来自",addr,data)
18         cmd,file_name = data.decode().split()
19         if os.path.isfile(file_name):
20           m = hashlib.md5()
21           f = open(file_name,‘rb‘)
22           file_size = os.stat(file_name).st_size
23           conn.send(str(file_size).encode())
24           ack = conn.recv(1024).decode()
25           print("确认:",ack)
26           for line in f:
27             m.update(line)
28             print("sending...")
29             conn.send(line)
30           f.close()
31           conn.send(m.hexdigest().encode())
32         print("发送完毕")
33 server.close()

ftp_client.py

 1 #!/usr/bin/env python3
 2 import socket,hashlib
 3 ip_address = ‘192.168.81.133‘
 4 port = 8888
 5 conn_address = (ip_address,port)
 6 client = socket.socket()
 7 client.connect(conn_address)
 8 while True:
 9     cmd = input("input your cmd:> ").strip()
10     if len(cmd) == 0:
11         continue
12     if cmd.startswith("get"):
13       client.send(cmd.encode(‘utf-8‘))
14       file_name = cmd.split()[1]
15       server_response = client.recv(1024)
16       print("收到回应:",server_response)
17       client.send("收到".encode(‘utf-8‘))
18       total_file_size = int(server_response.decode())
19       print("总的",total_file_size)
20       f = open(file_name + ‘.new‘,‘wb‘)
21       recov_file_size = 0
22       m = hashlib.md5()
23       while recov_file_size < total_file_size:
24           if total_file_size - recov_file_size > 1024:
25              size = 1024
26           else:
27              size =  total_file_size - recov_file_size
28              print("最后收",size)
29           #print("收数据...")
30           data = client.recv(size)
31           m.update(data)
32           recov_file_size += len(data)
33           f.write(data)
34       else:
35           print("收完了,大小",recov_file_size,total_file_size)
36           f.close()
37           print("服务器端发送的原MD5",client.recv(1024).decode())
38           print("自己算出来的最终MD5",m.hexdigest())
39 client.close()

开始学习SocketSever

SocketSever  是对socket 的再封装

最常用的是 TCP 和 UDP

继承关系:

创建socket server 步骤:

下面是重写的handle:

跟客户端所有的交互都是在handle 里处理的

每一个客户端请求都是实例化一个handle

继续修改handle

增加循环:

并增加判断客户端退出:

实践:

server:

 1 import socketserver
 2 class MyTCPHandler(socketserver.BaseRequestHandler):
 3     def handle(self):
 4         while True:
 5             try:
 6                 self.data = self.request.recv(1024).strip()
 7                 print("来自",self.client_address)
 8                 print(self.data)
 9                 self.request.send(self.data.upper())
10             except ConnectionResetError as e:
11                 print("客户端退出",e)
12                 break
13 if __name__ == "__main__":
14     HOST,PORT="localhost",9999
15     server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
16     server.serve_forever()

client:

 1 import  socket
 2 client = socket.socket() #声明socket类型,同时生成socket连接对象
 3 #client.connect((‘192.168.16.200‘,9999))
 4 client.connect((‘localhost‘,9999))
 5 while True:
 6     msg = input(">>:").strip()
 7     if len(msg) == 0:continue
 8     client.send(msg.encode("utf-8"))
 9     data = client.recv(10240)
10     print("recv:",data.decode())
11 client.close()

上面的并不支持多并发

到现在为止,只是写了一个一对一的服务器

下面写入多并发:

只改一个地方:

1 使用多线程 来实现

2 使用多进程实现:  (windos 下不行,不支持,但是linux  绝得好使)

介绍 socketserver的一些方法:

1 返回文件描述符  (系统自己调用,我们一般用不到)

2 处理单个请求,一般也用不到

3 一直收请求,直到收到一个明确的shutdown   (每0.5秒检测是否给我发了shutdown 的信号)

收到shutdown 后调用 service_action() 清理垃圾僵尸进程

4 告诉 server_forever() 关闭

5 地址的重用,不需要等待tcp断开

普通的sock server

server = socket.socket() #获得socket实例

server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  #重用地址

请求的三个过程,都可以自定义:

1 setup

2 handle

3 finish

时间: 2024-08-07 02:20:38

day8---多线程socket 编程,tcp粘包处理的相关文章

Socket编程--TCP粘包问题

TCP是个流协议,它存在粘包问题 产生粘包的原因是: TCP所传输的报文段有MSS的限制,如果套接字缓冲区的大小大于MSS,也会导致消息的分割发送. 由于链路层最大发送单元MTU,在IP层会进行数据的分片. 应用层调用write方法,将应用层的缓冲区中的数据拷贝到套接字的发送缓冲区.而发送缓冲区有一个SO_SNDBUF的限制,如果应用层的缓冲区数据大小大于套接字发送缓冲区的大小,则数据需要进行多次的发送. 粘包问题的解决 ①:发送定长包 这里需要封装两个函数: ssize_t readn(int

python/socket编程之粘包

python/socket编程之粘包 粘包: 只有TCP有尿包现象,UDP永远不会粘包. 首先需要掌握一个socket收发消息的原理 发送端可以是1k,1k的发送数据而接受端的应用程序可以2k,2k的提取数据,当然也有可能是3k或者多k提取数据,也就是说,应用程序是不可见的,因此TCP协议是面来那个流的协议,这也是容易出现粘包的原因而UDP是面向笑死的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任一字节的数据,这一点和TCP是很同的.怎样定义消息呢?认为对方一次

java socket编程解决粘包和丢包问题

##socket 丢包粘包解决方式 采用固定头部长度(一般为4个字节),包头保存的是包体的长度 header+body 包头+包体 思路是:先读出一个包头,得到包体的长度,解析出包体 public class SocketServer { public static void main(String args[]) { ServerSocket serverSocket; try { serverSocket = new ServerSocket(); serverSocket.bind(new

Socket编程实践(5) --TCP粘包问题与解决

TCP粘包问题 因为TCP协议是基于字节流且无边界的传输协议, 因此非常有可能产生粘包问题, 问题描写叙述例如以下 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvempmMjgwNDQxNTg5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" /> 对于Host A 发送的M1与M2两个各10K的数据块, Host B 接收数据的方式不确定, 有以下

SOCKET TCP 粘包及半包问题

大家在使用SOCKET通信编程的时候,一般会采用UDP和TCP两种方式:TCP因为它没有包的概念,它只有流的概念,并且因为发送或接收缓冲区大小的设置问题,会产生粘包及半包的现象. 场景: 服务端向连续发送三个"HelloWorld"(三次消息无间隔),那么客户端接收到的情况会有以下三种: 1)HelloWorld HelloWorld HelloWorld (客户端接收三次) 2)HelloWorldHelloWor ldHelloWorld (客户端接收两次) 3)HelloWorl

tcp粘包问题(封包)

tcp粘包分析     http://blog.csdn.net/zhangxinrun/article/details/6721495 解决TCP网络传输“粘包”问题(经典)       http://blog.csdn.net/zhangxinrun/article/details/6721508 粘包出现原因:在流传输中出现,UDP不会出现粘包,因为它有消息边界(参考Windows 网络编程)1 发送端需要等缓冲区满才发送出去,造成粘包2 接收方不及时接收缓冲区的包,造成多个包接收 解决办

TCP粘包问题分析和解决(全)

TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小.数据量小的数据,合并成一个大的数据块,然后进行封包.这样,接收端,就难于分辨出来了,必须提供科学的拆包机制. 对于UDP,不会使用块的合并优化算法,这样,实际上目前认为,是由于UDP支持的是一对多的模式,

netty 解决TCP粘包与拆包问题(二)

TCP以流的方式进行数据传输,上层应用协议为了对消息的区分,采用了以下几种方法. 1.消息固定长度 2.第一篇讲的回车换行符形式 3.以特殊字符作为消息结束符的形式 4.通过消息头中定义长度字段来标识消息的总长度 一.采用指定分割符解决粘包与拆包问题 服务端 1 package com.ming.netty.nio.stickpack; 2 3 4 5 import java.net.InetSocketAddress; 6 7 import io.netty.bootstrap.ServerB

Netty学习之TCP粘包/拆包

一.TCP粘包/拆包问题说明,如图 二.未考虑TCP粘包导致功能异常案例 按照设计初衷,服务端应该收到100条查询时间指令的请求查询,客户端应该打印100次服务端的系统时间 1.服务端类 package com.phei.netty.s2016042302; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitial

TCP粘包, UDP丢包, nagle算法

一.TCP粘包 1. 什么时候考虑粘包 如果利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题(因为只有一种包结构,类似于http协议,UDP不会出现粘包现象).关闭连接主要要双方都发送close连接(参考tcp关闭协议).如:A需要发送一段字符串给B,那么A与B建立连接,然后发送双方都默认好的协议字符如"hello give me sth abour yourself",然后B收到报文后,就将缓冲区数据接收,然后关闭连接,这样粘包问