Socket

一:socket介绍

#Socket是 应用层 与 TCP/IP 协议族通信的中间软件的抽象层,Socket的设计模式是门面模式,它将极其复杂的TCP/IP 封装隐藏在Socket
  接口的后面。程序员只需通过Socket的接口进行调用即可

import socket
# tcp/ip套字连接
soc1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

# udp/ip套字连接
soc2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#服务端套字函数:
soc1.bind(('ip','端口')) #绑定主机和IP地址
soc1.listen(5)  # 开始监听
soc1.accept()   #被动式TCP客户端连接,阻塞等待连接的到来


#客户端套字函数:
soc1.connect(('ip','端口')) #主动初始化TCP服务器连接
soc1.connect_ex(('ip','端口'))  #connect()的拓展,出错时返回出错码,而不是抛出异常


#公共套字连接函数,UDP和TCP
soc1.recv(1024) # 接受TCP数据,参数为缓冲区的大小
soc1.send(data='abc') # 发送TCP数据,参数为要发送的数据,另外,send在待发送数据量大于己端缓存区剩余空间时,
              数据会造成丢失

soc1.sendall(data='abc') # 发送TCP数据,参数为要发送的数据,send的加强版,在待发送数据量大于己端缓存区剩余空间时, 循环调用send发送数据,直到数据发送完为止,而且不会造成数据丢失 soc1.recvfrom(1024) #接受udp数据,参数为缓存区大小 soc1.sendto(data='abc') #发送udp数据, 参数为要发送的数据 soc1.getpeername() # 连接到当前套接字的远端的地址 soc1.getsockname() #当前套接字的地址 soc1.getsockopt() #返回指定套接字的参数 soc1.setsockopt() #设置指定套接字的参数 soc1.close() #关闭套接字 #面向锁的套接字函数 soc1.setblocking() # 设置套接字的阻塞与非阻塞模式 soc1.settimeout() # 设置阻塞套接字操作的超时时间 soc1.gettimeout() #得到阻塞套接字操作的超时时间 # 面向文件的套接字函数 soc1.fileno() #套接字的文件描述符 soc1.makefile() #创建一个与该套接字相关的文件

测试小例子TCP:

from socket import *
s=socket(AF_INET,SOCK_DGRAM)
s.bind(('127.0.0.1',8081))
s.listen(2) #开始监听,并指定能同时处理多少个客户端的连接请求
conn,addr=s.accept()
while True:
    '''
    # 设置缓冲区大小为10个字节,一个 ascii 字符的大小为1个字节,例如一个字母的大小为1个,
    所以能一次接收10个ascii字符,如果客户端发送过来的数据过大,那么它会分多次进行接受
    '''
    data=conn.recv(10)
    print('==================server========================')
    print(data)
    conn.sendall(data.upper())
conn.close()
phone.close()
Server端
from socket import *

phone=socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8081))

while True:
    msg=input('>>: ').strip()
    phone.send(msg.encode('utf-8'))
    print('==================client================')
    '''
     #设置缓冲区大小为 1024B,也就是能一次接受服务端发送过来的数据大小为 1kb,
     如果服务端发送过来的数据过大,那么会分多次进行接受
    '''
    data=phone.recv(1024)
    print(data)
client端

测试小例子UDP:

#UDP是无链接的,先启动哪一端都不会报异常,另外,还可以同时跟多个服务端通信
from socket import *
s=socket(AF_INET,SOCK_DGRAM)
s.bind(('127.0.0.1',8088))
while True:
    data,addr=s.recvfrom(10) # 返回值为 客户端发送的数据 和 套字连接地址
    print('==================server========================')
    print(data)
    s.sendto(data,addr)
conn.close()
phone.close()
Server
from socket import *

s=socket(AF_INET,SOCK_DGRAM)
s.connect(('127.0.0.1',8088))

while True:
    msg=input('输入发送的数据').strip()
    s.sendto(msg.encode('utf-8'),('127.0.0.1',8088))
    print('==================client================')
    data=s.recvfrom(1024)
    print(data)

client
cilent

 基于UDP实现时间服务器:

from socket import *
from time import strftime
server = socket(AF_INET,SOCK_DGRAM)
server.bind(('127.0.0.1', 89))

while True:
    data, addr = server.recvfrom(1024)
    if not data:
        time_fmt = '%Y-%m-%d'
    else:
        time_fmt = data.decode('utf-8')
    back_msg = strftime(time_fmt)
    server.sendto(back_msg.encode('utf-8'), addr)
server.close()
time_server
from socket import *

client=socket(AF_INET,SOCK_DGRAM)
# client.bind(('127.0.0.1','89'))

while True:
    msg=input('请输入时间格式(例%Y%m%d)>>:').strip()
    client.sendto(msg.encode('utf-8'), ('127.0.0.1',89))

    data=client.recv(1024)

    print(data.decode('utf-8'))

tcp_client.close()
time_server

二:粘包

#粘包的产生:

 

 粘包有两种:

#第一种粘包
发送端需要等缓冲区满才发送出去,但由于发送数据时间间隔很短,数据又很小,会造成两块数据粘在一起,这就是粘包!!!!!!!!

#例如生活中的场景:有A、B两个地点,A点有两堆0.5吨的泥沙,需要将A地点的泥沙用车运送到B地点, 车的载重是0.5吨。在将第一堆泥沙
铲到车后,由于运送速度和A点工人的干活速度非常快,第一堆泥沙运到B点后,B点还没来的急处理,立马又运了第二堆沙子过来,将两堆
沙子合在了一起,变成了1吨
from socket import *
#服务端
server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',888))
server.listen(5)
conn,addr=server.accept()

data1=conn.recv(10)
data2=conn.recv(10)

print('----->',data1.decode('utf-8')) #打印 hellotom
print('----->',data2.decode('utf-8'))

conn.close()


#客户端
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(('127.0.0.1',888)
)
s.send('hello'.encode('utf-8'))
s.send('tom'.encode('utf-8'))
第一种粘包
#第二种粘包
#接受方来不及接受缓冲区的包,客户端发送了一个数据段,服务端只收了一小部分,服务端下次再收的时候依旧是从缓冲区拿上次
遗留的数据,进而产生粘包
#第一个篮子能装2个苹果,第二个篮子能装10个苹果,第一次送来了5个苹果,第一个篮子装了两个,第二个篮子装了3个,第二次送来
5个苹果,装在了第二个篮子里。由此造成数据不独立粘在了一起
from socket import *
#服务端
server=socket(AF_INET,SOCK_STREAM)
server.bind(('127.0.0.1',888))
server.listen(5)
conn,addr=server.accept()

data1=conn.recv(2)
data2=conn.recv(10)

print('----->',data1.decode('utf-8')) #打印 hellotom
print('----->',data2.decode('utf-8'))

conn.close()


#客户端
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(('127.0.0.1',888)
)
s.send('hello'.encode('utf-8'))
s.send('tom'.encode('utf-8'))

第三:粘包解决方案

解决粘包问题,需要知道 struct 模块
https://www.cnblogs.com/gala/archive/2011/09/22/2184801.html
import socket,struct
import subprocess
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

s.bind(('127.0.0.1',8080))

s.listen(5)

while True:
    conn,addr=s.accept()
    while True:
        cmd=conn.recv(1024)
        if not cmd:break
        res=subprocess.Popen(cmd.decode('utf-8'),
                             shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        err=res.stderr.read()
        if err:
            back_msg=err
        else:
            back_msg=res.stdout.read()

        conn.send(struct.pack('i',len(back_msg))) #发送数据的长度
        conn.sendall(back_msg) #发真实的内容

    conn.close()
Server
import socket,time,struct

s=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:continue
    if msg == 'quit':break

    s.send(msg.encode('utf-8'))



    l=s.recv(4) # 解或压 缓冲区空间必须为4B 以上的大小
    x=struct.unpack('i',l)[0] #将二进制数据变成原有的十进制数
    print(x)
    resu=0
    data = None
    #以下将数据进行分次取出,然后进行数据的拼接
    while resu < x:
        teme_data = s.recv(100)
        data += teme_data
        resu += len(teme_data)

    print(data.decode('gbk'))
Client
粘包问题小结:只有TCP才有粘包问题,TCP的特质是数据不会丢失,但UDP会丢失数据,安全性比UDP高

 第四:FTP案例

from socket import *
import struct,json
auth_dict = {}
msg = {}
def auth(func):
    def __b(self):
        user = auth_dict['user']
        pasd= auth_dict['pass']
        if user == 'tom' and pasd == '123':
            func(self)
        else:
            msg['msg'] = '帐号或者密码错误' # 如果密码错误写入字典
    return __b


class Conn:
    def __init__(self, ip, port):
        self.ip = ip
        self.port = port

    def run(self):

        global auth_dict
        self.server = socket(AF_INET, SOCK_STREAM)
        self.server.bind((self.ip, self.port))
        self.server.listen(5)
        while True:
            self.conn, self.addr = self.server.accept()  # 阻塞,等待访问
            while True:
                class FtpServer:

                    def __init__(self, dict_data):
                        self.dict_data = dict_data

                    @auth
                    def run(self):
                        with open('33.mp4', 'wb+') as f:
                            f.write(self.dict_data['data'])
                        return

                    def put(self, filename):
                        pass

                head = self.conn.recv(4) # 接受数据长度
                if not head:
                    continue
                print(head)
                hean_len = struct.unpack('i', head)[0]
                hean_json =  self.conn.recv(hean_len)  # 接收字典
                head_dict = json.loads(hean_json) # 转换字典
                resu = 0
                data = b''
                while resu < head_dict['size']:
                    temp_data = self.conn.recv(10000000)
                    data += temp_data
                    resu += len(temp_data)
                auth_dict = head_dict
                f = FtpServer(data)
                f.run()
                if msg:
                    self.conn.send(msg['msg'].encode('gbk')) #将错误信息返回客户端


c = Conn('127.0.0.1',889)
c.run()
FTP_server
from socket import *
import struct,json,os


class FtpClient:
    def __init__(self,ip, port):
        self.ip = ip
        self.port = port

    def run(self):

        self.client = socket(AF_INET, SOCK_STREAM)
        self.client.connect((self.ip, self.port))
        while True:
            order = input('输入你的操作:').strip() # put xxx.txt
            if not order:continue
            l_str = order.split() #切割
            print(l_str)
            if hasattr(self, l_str[0]):
                fun = getattr(self, l_str[0])
                fun(l_str)

    def put(self, args):
        self.filename = args[1]
        self.cmd = args[0]
        if not os.path.isfile(self.filename):
            return
        else:
            self.filesize = os.path.getsize(self.filename)
            head_dict = {'cmd': self.cmd, 'user': 'tom', 'pass': '1234', 'size': self.filesize}
            head_bytes = bytes(json.dumps(head_dict), encoding='gbk')  # 序列化 转化成 二进制

            with open(self.filename, 'rb') as f:
                data = f.read()
                print(data)
            self.client.send(struct.pack('i', len(head_bytes)))
            self.client.sendall(head_bytes)
            self.client.sendall(data)

            msg = self.client.recv(1000).decode('gbk')
            print(msg) #将错误信息打印
            
            while True:pass
    def loging(self):
        pass

    def send(self):
        pass




f = FtpClient('127.0.0.1',889)
f.run()
FTP_client

 

posted @ 2018-07-03 04:20  lei-jia-ming  阅读(176)  评论(0)    收藏  举报