Socket函数编程(二)

粘包问题

1.修改数据长度:

client端

 1 import socket
 2
 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 #拨通电话
 5 ip_port = (‘127.0.0.1‘,8081)
 6 phone.connect(ip_port)
 7
 8 while True:
 9     #发消息
10     cmd = input(‘>>: ‘).strip()
11     if not cmd:continue
12     phone.send(bytes(cmd,encoding=‘utf-8‘))
13     #收消息
14     data = phone.recv(1024)  #8192
15     print(data.decode(‘gbk‘))
16 phone.close()

#输出 长于1024字节的不行

server端

import socket
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #SOCK_STREAM tcp
# ip_port=(‘127.0.0.1‘,808)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
ip_port=(‘127.0.0.1‘,8081)
phone.bind(ip_port)

phone.listen(5)   #开机
while True:
    conn,client_addr=phone.accept()

    while True:
        try:
            cmd = conn.recv(1024)
            if not cmd:break
            res = subprocess.Popen(cmd.decode(‘utf-8‘),
                                   shell=True,
                                   stderr=subprocess.PIPE,
                                   stdout=subprocess.PIPE
                                   )
            # err=res.stderr.read()
            # if err:
            #     cmd_res=err
            # else:
            #     cmd_res=res.stdout.read()
            conn.send(res.stdout.read())
            conn.send(res.stderr.read())
        except Exception:
            break

    conn.close()
phone.close()

client端

1 import socket
2 import subprocess
3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
4 #拨通电话
5 ip_port = (‘127.0.0.1‘,8080)
6 phone.connect(ip_port)
7
8 phone.send(‘helloworld‘.encode(‘utf-8‘))
9 phone.send(‘SB‘.encode(‘utf-8‘))

#输出

第一个包 b‘helloworldSB‘
第二个包 b‘‘

服务器端

 1 import  socket
 2 import subprocess
 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #SOCK_STREAM tcp
 4 ip_port=(‘127.0.0.1‘,8080)
 5 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
 6
 7 phone.bind(ip_port)
 8 phone.listen(5)   #开机
 9 conn,client_addr=phone.accept()
10
11 data1=conn.recv(1024)
12 data2=conn.recv(1024)
13
14 print(‘第一个包‘,data1)
15 print(‘第二个包‘,data2)

改进

2.修改时间长度

client端

 1 import socket,time
 2 import subprocess
 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 #拨通电话
 5 ip_port = (‘127.0.0.1‘,8080)
 6 phone.connect(ip_port)
 7
 8 phone.send(‘helloworld‘.encode(‘utf-8‘))    #客户端都是发到自己的缓存里了
 9 time.sleep(3)           #网络延迟没有三秒长
10 phone.send(‘SB‘.encode(‘utf-8‘))

server端

 1 import  socket
 2 import subprocess,time
 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)       #SOCK_STREAM tcp
 4 ip_port=(‘127.0.0.1‘,8080)
 5 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
 6
 7 phone.bind(ip_port)
 8 phone.listen(5)   #开机
 9 conn,client_addr=phone.accept()
10
11 data1=conn.recv(1)          #conn.recv(1024)
12 time.sleep(5)
13 data2=conn.recv(1024)
14
15 print(‘第一个包‘,data1)
16 print(‘第二个包‘,data2)

TCP流式协议,

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

此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

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

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

所以,发包时一定要给包定义一个头部。

3.stuct模块解决粘包

为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据

报头: 1.固定长度 2.包含对发送数据的描述信息

自定义报头(普通)

client端

 1 import socket
 2 import struct
 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 4 #拨通电话
 5 ip_port = (‘192.168.16.134‘,8080)
 6 phone.connect(ip_port)
 7 #通信循环
 8 while True:
 9     #发消息
10     cmd = input(‘>>: ‘).strip()
11     if not cmd:continue
12     phone.send(bytes(cmd,encoding=‘utf-8‘))
13     #收报头
14     baotou = phone.recv(4)          #8192
15     data_size=struct.unpack(‘i‘,baotou)[0]
16
17     #收数据
18     recv_size=0
19     recv_data=b‘‘
20     while recv_size < data_size:
21         data=phone.recv(1024)
22         recv_size+=len(data)
23         recv_data+=data
24     print(recv_data.decode(‘utf-8‘))
25 phone.close()

server端

 1 #/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 import socket
 4 import struct
 5 import subprocess
 6 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 7 ip_port=(‘192.168.16.134‘,8080)
 8
 9 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
10 phone.bind(ip_port)
11
12 phone.listen(5)
13 while True:
14     conn,client_addr=phone.accept()
15
16     while True:
17         try:
18             cmd = conn.recv(1024)
19             if not cmd:break
20             res = subprocess.Popen(cmd.decode(‘utf-8‘),
21                                    shell=True,
22                                    stderr=subprocess.PIPE,
23                                    stdout=subprocess.PIPE
24                                    )
25             out_res=res.stdout.read()
26             err_res=res.stderr.read()
27             data_size=str(len(out_res)+len(err_res)).encode(‘utf-8‘)
28             #发送报头
29             conn.send(struct.pack(‘i‘,data_size))
30             #发送数据部分
31             conn.send(out_res)
32             conn.send(err_res)
33         except Exception:
34             break
35
36     conn.close()
37 phone.close()

struct.error: ‘i‘ format requires -2147483648 <= number <= 2147483647 #这个是范围

自定义报头(jason)

server端

 1 #/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 import socket
 4 import struct
 5 import subprocess
 6 import json
 7 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 8 ip_port=(‘192.168.16.134‘,8080)
 9
10 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
11 phone.bind(ip_port)
12
13 phone.listen(5)
14 while True:
15     conn,client_addr=phone.accept()
16
17     while True:
18         try:
19             cmd = conn.recv(1024)
20             if not cmd:break
21             res = subprocess.Popen(cmd.decode(‘utf-8‘),
22                                    shell=True,
23                                    stderr=subprocess.PIPE,
24                                    stdout=subprocess.PIPE
25                                    )
26             out_res=res.stdout.read()
27             err_res=res.stderr.read()
28             data_size=len(out_res)+len(err_res)
29             head_dic={‘data_size‘:data_size}
30             head_json=json.dumps(head_dic)
31         head_bytes=head_json.encode(‘utf-8‘)
32         #part1:先发报头的长度
33         head_len=len(head_bytes)
34         conn.send(struct.pack(‘i‘,head_len))
35             #part2:再发送报头
36             conn.send(head_bytes)
37             #part3:最后发送数据部分
38             conn.send(out_res)
39             conn.send(err_res)
40         except Exception:
41             break
42
43     conn.close()
44 phone.close()

client端

 1 import socket
 2 import struct
 3 import json
 4 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 5 #拨通电话
 6 ip_port = (‘192.168.16.134‘,8080)
 7 phone.connect(ip_port)
 8 #通信循环
 9 while True:
10     #发消息
11     cmd = input(‘>>: ‘).strip()
12     if not cmd:continue
13     phone.send(bytes(cmd,encoding=‘utf-8‘))
14
15     #part1:先收报头的长度
16     head_struct=phone.recv(4)
17     print(struct.unpack(‘i‘,head_struct))
18     head_len=struct.unpack(‘i‘,head_struct)[0]
19
20     #part2:再收报头
21     head_bytes=phone.recv(head_len)
22     head_json=head_bytes.decode(‘utf-8‘)
23
24     head_dic=json.loads(head_json)
25     print(head_dic)
26     data_size=head_dic[‘data_size‘]
27
28     #part3收数据
29     recv_size=0
30     recv_data=b‘‘
31     while recv_size < data_size:
32         data=phone.recv(1024)
33         recv_size+=len(data)
34         recv_data+=data
35     print(recv_data.decode(‘utf-8‘))
36 phone.close()
报头类似于字典,先做字典,字典转成json格式,保证发过去还有结构的。把json字符串转成bytes格式,有了它可以发送报头的。

发送时:服务器端struct模块把报头长度打成bytes,先发报头长度,在发报头,再发真实数据。接收时:客户端通过struct模块拿到报头长度,再recv收到报头长度,解码,反序列化得到字典,再从字典里拿到真实数据

FTP上传下载

服务端

import socket
import struct
import json
import subprocess
import os

class MYTCPServer:
    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    allow_reuse_address = False

    max_packet_size = 8192

    coding=‘utf-8‘

    request_queue_size = 5

    server_dir=‘file_upload‘

    def __init__(self, server_address, bind_and_activate=True):
        """Constructor.  May be extended, do not override."""
        self.server_address=server_address
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if bind_and_activate:
            try:
                self.server_bind()
                self.server_activate()
            except:
                self.server_close()
                raise

    def server_bind(self):
        """Called by constructor to bind the socket.
        """
        if self.allow_reuse_address:
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)
        self.server_address = self.socket.getsockname()

    def server_activate(self):
        """Called by constructor to activate the server.
        """
        self.socket.listen(self.request_queue_size)

    def server_close(self):
        """Called to clean-up the server.
        """
        self.socket.close()

    def get_request(self):
        """Get the request and client address from the socket.
        """
        return self.socket.accept()

    def close_request(self, request):
        """Called to clean up an individual request."""
        request.close()

    def run(self):
        while True:
            self.conn,self.client_addr=self.get_request()
            print(‘from client ‘,self.client_addr)
            while True:
                try:
                    head_struct = self.conn.recv(4)
                    if not head_struct:break

                    head_len = struct.unpack(‘i‘, head_struct)[0]
                    head_json = self.conn.recv(head_len).decode(self.coding)
                    head_dic = json.loads(head_json)

                    print(head_dic)
                    #head_dic={‘cmd‘:‘put‘,‘filename‘:‘a.txt‘,‘filesize‘:123123}
                    cmd=head_dic[‘cmd‘]
                    if hasattr(self,cmd):
                        func=getattr(self,cmd)
                        func(head_dic)
                except Exception:
                    break

    def put(self,args):
        file_path=os.path.normpath(os.path.join(
            self.server_dir,
            args[‘filename‘]
        ))

        filesize=args[‘filesize‘]
        recv_size=0
        print(‘----->‘,file_path)
        with open(file_path,‘wb‘) as f:
            while recv_size < filesize:
                recv_data=self.conn.recv(self.max_packet_size)
                f.write(recv_data)
                recv_size+=len(recv_data)
                print(‘recvsize:%s filesize:%s‘ %(recv_size,filesize))

tcpserver1=MYTCPServer((‘127.0.0.1‘,8080))

tcpserver1.run()

#下列代码与本题无关
class MYUDPServer:

    """UDP server class."""
    address_family = socket.AF_INET

    socket_type = socket.SOCK_DGRAM

    allow_reuse_address = False

    max_packet_size = 8192

    coding=‘utf-8‘

    def get_request(self):
        data, client_addr = self.socket.recvfrom(self.max_packet_size)
        return (data, self.socket), client_addr

    def server_activate(self):
        # No need to call listen() for UDP.
        pass

    def shutdown_request(self, request):
        # No need to shutdown anything.
        self.close_request(request)

    def close_request(self, request):
        # No need to close anything.
        pass

服务端

import socket
import struct
import json
import os

class MYTCPClient:
    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    allow_reuse_address = False

    max_packet_size = 8192

    coding=‘utf-8‘

    request_queue_size = 5

    def __init__(self, server_address, connect=True):
        self.server_address=server_address
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if connect:
            try:
                self.client_connect()
            except:
                self.client_close()
                raise

    def client_connect(self):
        self.socket.connect(self.server_address)

    def client_close(self):
        self.socket.close()

    def run(self):
        while True:
            inp=input(">>: ").strip()
            if not inp:continue
            l=inp.split()
            cmd=l[0]
            if hasattr(self,cmd):
                func=getattr(self,cmd)
                func(l)

    def put(self,args):
        cmd=args[0]
        filename=args[1]
        if not os.path.isfile(filename):
            print(‘file:%s is not exists‘ %filename)
            return
        else:
            filesize=os.path.getsize(filename)

        head_dic={‘cmd‘:cmd,‘filename‘:os.path.basename(filename),‘filesize‘:filesize}
        print(head_dic)
        head_json=json.dumps(head_dic)
        head_json_bytes=bytes(head_json,encoding=self.coding)

        head_struct=struct.pack(‘i‘,len(head_json_bytes))
        self.socket.send(head_struct)
        self.socket.send(head_json_bytes)
        send_size=0
        with open(filename,‘rb‘) as f:
            for line in f:
                self.socket.send(line)
                send_size+=len(line)
                print(send_size)
            else:
                print(‘upload successful‘)

client=MYTCPClient((‘192.168.16.134‘,8080))

client.run()

客户端

时间: 2024-10-10 06:41:14

Socket函数编程(二)的相关文章

C# Socket基础(二) 之 服务器异步接收消息

ManualResetEvent reviceManager = new ManualResetEvent(false); 1 public void args_Completed(object sender, SocketAsyncEventArgs e) 2 { 3 //监听完成客户端的请求,一但监听到返回新的套接字 4 var clientSocket = e.AcceptSocket; 5 //启动线程获取客户端发来的消息 6 if (clientSocket == null) retu

一起talk C栗子吧(第一百四十八回:C语言实例--socket通信二)

各位看官们,大家好,上一回中咱们说的是socket通信的例子,这一回咱们继续说该例子.闲话休提,言归正转.让我们一起talk C栗子吧! 看官们,我们在上一回中介绍了套接字的通信步骤,限于时间的原因, 我们只介绍了流套接字的通信步骤.这一回中我们将介绍数据报套接字的通信步骤. 下面是客户端和服务器端实现通信的详细步骤: 客户端实现通信的步骤: 1.创建客户端套接字(socket): 2.设置客户端套接字的属性:域,类型和协议: 3.通过操作客户端套接字来实现客户端与服务器的通信(sendto,r

10 Socket(二)

1. 作用域 1.1 作用域 代码1 if 1 == 1: name = 'alex' for i in range(10): name = i print(name) # Java/C# 不可以 # Python/JavaScript 可以 # Python中无块级作用域 代码2:函数 def func(): name = 'alex' print(name) def func(): name = 'alex' func() print(name) # Python中以函数为作用域 代码3:

一起talk C栗子吧( 第一百五十回:C语言实例--socket通信接口二)

各位看官们,大家好,上一回中咱们说的是socket通信的例子,这一回咱们继续说该例子.闲话休提,言归正转.让我们一起talk C栗子吧! 看官们,我们在上一回中介绍了套接字通信过程中使用的系统调用,这些系统调用具有通用性,在任何类型的套接字通信过程中都可以使用它们.今天,我们将介绍一些专门用来通过套接字发送和接收数据的系统调用.这些系统调用的功能十分相似,只是在操作的细节上不相同.接下来我们分别介绍他们. 发送数据的系统调用 ssize_t send(int sockfd, const void

Java之------socket系列(二)UDP

☆ UDP: 将数据及源和目的封装成数据包中,不需要建立连接 每个数据报的大小在限制在64k内 因无连接,是不可靠协议 不需要建立连接,速度快 DatagramSocket和 DatagramPacket类 UDP传输: DatagramSocket与DatagramPacket 建立发送端,接收端. 建立数据包. 调用Socket的发送接收方法. 关闭Socket. 发送端与接收端是两个独立的运行程序. UDP传输编程: ☆发送端 在发送端,要在数据包对象中明确目的地IP及端口. Datagr

socket网络(二)

作用域 python/js语言中,无块级作用域 if 1 == 1: name = 'alex' print(name) python中以函数为作用域 def func(): name = 'alex' print(name) 作用域链, name = 'alex' def f1(): print(name) def f2(): name = 'Eric' f1() f2()

Python 基础之socket编程(二)

Python 基础之socket编程(二) 昨天只是对socket编程做了简单的介绍,只是把socket通信的框架搭建起来,要对其中的功能进行进一步的扩充,就来看看今天的料哈! 一.基于tcp的套接字 1. tcp的服务端 ss = socket() #创建服务器套接字 ss.bind() #把地址绑定到套接字 ss.listen() #监听链接 inf_loop: #服务器无限循环 cs = ss.accept() #接受客户端链接 comm_loop: #通讯循环 cs.recv()/cs.

Python学习之旅 —— socket篇(一)

一.什么是socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用[打开][读写][关闭]模式来操作.socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO.打开.关闭) socket和file的区别: file模块是针

mysql Can&#39;t connect to local MySQL server through socket &#39;/var/lib/mysql/mysql.sock&#39; (2)

错误原因:/var/lib/mysql目录中socket文件不存在.连接mysql服务器有两种方式:tcp连接,通过socket文件连接.通过socket文件,启动mysql服务,mysql服务会自动生成一个sock文件,生成的sock文件默认放在 --datadir=/var/lib/mysql,mysql默认从/var/lib/mysql目录读取sock文件. 解决办法:1.看看/var/lib/mysql/mysql 有没有mysql.sock文件2.没有mysql.sock,重启mysq