python-socket

# 什么是 Socket?

#  Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯。

 

#  Python 中,我们用 socket()函数来创建套接字,语法格式如下:

#  socket.socket([family[, type[, proto]]])
  • family: 套接字家族可以使AF_UNIX或者AF_INET
  • type: 套接字类型可以根据是面向连接的还是非连接分为SOCK_STREAMSOCK_DGRAM
  • protocol: 一般不填默认为0.

 

s.connect()    主动初始化TCP服务器连接,。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
s.connect_ex()    connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
客户端函数
s.bind()    绑定地址(host,port)到套接字, 在AF_INET下,以元组(host,port)的形式表示地址。
s.listen()    开始TCP监听。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,大部分应用程序设为5就可以了。
s.accept()    被动接受TCP客户端连接,(阻塞式)等待连接的到来
服务器端函数
s.recv()    接收TCP数据,数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。
s.send()    发送TCP数据,将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。
s.sendall()    完整发送TCP数据,完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
s.recvfrom()    接收UDP数据,与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
s.sendto()    发送UDP数据,将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。
s.close()    关闭套接字
s.getpeername()    返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
s.getsockname()    返回套接字自己的地址。通常是一个元组(ipaddr,port)
s.setsockopt(level,optname,value)    设置给定套接字选项的值。
s.getsockopt(level,optname[.buflen])    返回套接字选项的值。
s.settimeout(timeout)    设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
s.gettimeout()    返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。
s.fileno()    返回套接字的文件描述符。
s.setblocking(flag)    如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。
s.makefile()    创建一个与该套接字相关连的文件
socket-公共函数

 

 

一个简单的socket通信实例
import socket


#   简单的socket例子。

#   声明网络协议,并创建socket对象。
client = socket.socket()
#   链接服务器
client.connect(('127.0.0.1',8088))
while True:
    user_input = input('>>:').strip()
    if not user_input:continue
    if user_input == 'exit':
        break
    #   发送数据,为了支持中文encode.
    client.send(user_input.encode('utf-8'))
    #   接收服务返回的数据
    ret = client.recv(1024)
    print('recv:',ret.decode())
#   关闭socket。
client.close()
socket_client.py
import socket

#   声明网络协议,并创建socket对象。
server = socket.socket()
#   绑定监听端口
server.bind(('127.0.0.1',8088)) # 绑定监听端口
#   监听
server.listen() #监听


"""
    第一个循环是持续等待请求数据。
    第二个循环是持续回复客户端信息。
    如果客户端传输数据为空,则断开对客户端的回复,跳回等待接收请求部分。
"""
while True:
    print('开始等待请求...')
    #   等待请求,connect是客户端连接服务器生产的实例,addr是地址信息
    connect,addr = server.accept()
    print(connect, addr)
    while True:

        #   接收客户端发送的数据
        client_data = connect.recv(1024)
        #   如果发送数据为空,则跳出继续接待客户端部分,回到等待请求部分。
        if not client_data:
            break

        print('recv:',client_data.decode())
        #   给客户端发送回复信息。
        connect.send(client_data)
socket_server.py

 

 

一个利用socket通信实现SSH执行linux命令的例子

  2.1 客户端向服务器发送执行命令
  2.2 服务器接受客户端命令并执行命令,获取执行后结果
  2.3 服务器向客户端发送执行命令结果的数据包大小。
  2.4 客户端接收数据大小,并向服务器端发送可以接收数据的确认信息。
  2.5 服务器接收到确认信息,给客户端发送数据。
  2.6 客户端接收并做处理显示结果

import socket


#   简单的socket通信实现ssh例子。

#   声明网络协议,并创建socket对象。
client = socket.socket()
#   链接服务器
client.connect(('127.0.0.1',8088))
while True:
    user_input = input('>>:').strip()
    if not user_input:continue
    if user_input == 'exit':
        break
    #   python3 支持发送字节
    client.send(user_input.encode())
    #   接收服务返回的数据。
    ret_size = client.recv(1024)
    print(ret_size)
    client.send('be ready'.encode('utf-8')) #  向服务器发送可以接受数据了。
    reply_size = 0
    reply_data = b''
    while reply_size != int(ret_size):

        ret_data = client.recv(1024)
        #   这里必要len()解码后的长度。因为服务端len()的便是未编码的长度。注意!!!!
        #   注意这里中文字符和英文字符len()长度问题。
        reply_size += len(ret_data.decode())
        reply_data += ret_data
    else:
        print(reply_size)
        print('over')
    print(reply_data.decode())

#   关闭socket。
client.close()
socket_ssh_client.py
import socket,os


#   声明网络协议,并创建socket对象。
server = socket.socket()
#   绑定监听端口
server.bind(('127.0.0.1',8088)) # 绑定监听端口
#   监听
server.listen() #监听


"""
    第一个循环是持续等待请求数据。
    第二个循环是持续回复客户端信息。
    如果客户端传输数据为空,则断开对客户端的回复,跳回等待接收请求部分。
"""
while True:
    print('开始等待请求...')
    #   等待请求,connect是客户端连接服务器生产的实例,addr是地址信息
    connect,addr = server.accept()
    print(connect, addr)
    while True:

        #   接收客户端发送的数据
        client_data = connect.recv(1024)

        #   如果发送数据为空,则跳出继续接待客户端部分,回到等待请求部分。
        if len(client_data) == 0:
            break


        res = os.popen(client_data.decode()).read()
        if not res:
            res = 'common not found'

        #   给客户端发送回复信息。
        #   如果是linux上,注意socket 粘包的情况。
        connect.send(str(len(res)).encode('utf-8')) #   先发送数据大小,保证客户端效验数据的完整性。
        """
        为了解决socket粘包的情况,在发送数据包大小和发送数据之间,再和客户端做一个交互,
        把缓冲区隔开,socket粘包就完美解决。
        
        """
        client_ack = connect.recv(1024)
        connect.send(res.encode('utf-8'))#  发送数据。
socket_ssh_server.py

 

 

通过Socket实现文件下载

        3.1    客户端发送下载请求。
    3.2    服务器检查下载文件存不存在。
    3.3 服务器向客服端发送文件大小。
    3.4 客户端接收文件大小数据,向服务器端发送可以进行下一步信息。
    3.5    服务器边打开文件,边发送数据,并在边打开文件期间生成MD5效验值。
    3.6 客户端接收数据同时生成客户端文件MD5效验码,客户端接收到MD5效验值与客户端MD5做对比,客户端根据对比结果做操作,之后向服务器发送对比结果。
    3.7 服务器接收到对比结果做对应操作。
import socket,hashlib,sys


"""socket客户端设置"""
client = socket.socket()
client.connect(('127.0.0.1',8999))

while True:
    input_value = input('<<:').strip()
    if not input_value:continue
    if input_value.startswith('get'):

        if  ' ' not in input_value or not input_value.split(' ')[1]:
            print('command error')
            continue
        file_name = input_value.split(' ')[1]

        """[发送] 用户输入值"""
        client.send(input_value.encode('utf-8'))

        """[接收] 应答,如果不报错,是文件大小,报错则打印报错信息"""
        data_size = client.recv(1024).decode()
        if len(data_size)==0 or 'error' in data_size:
            print(data_size.decode())
            continue

        """[发送] 准备接收数据信息"""
        client.send('be_ready'.encode('utf-8'))

        """
            [接收] 数据并且生成文件MD5
            注意:为了减少两端的交互,所以在发送数据之后着发送了文件MD5值。
            但是data_size只是文件大小,那么怎么分开接收文件数据和文件MD5呢?
            解:
            因为文件MD5是在数据末尾部分,所以while循环接收数据的最后一次,一定文件数据和文件MD5混合成的,
            只要区分开最后一次中,文件数据占了多少,client.recv()接收这一部分即可。
            而,再client.recv()一次,就只剩下文件MD5了。
        """
        rev_size = 0
        data_size = int(data_size)
        m = hashlib.md5()
        f = open('1_'+file_name,'wb')   #   测试用文件名。
        while rev_size < data_size:
            if data_size-rev_size > 1024:
                #   说明不是最后一次
                size = 1024
            else:
                #   最后一次,剩下的文件数据多少就收多少
                size = data_size-rev_size
            file_data = client.recv(size)
            m.update(file_data)
            rev_size += len(file_data)
            f.write(file_data)
        f.close()

        client_file_md5 = m.hexdigest()
        server_file_md5 = client.recv(1024).decode()


        print('client:{0} - server:{1}'.format(client_file_md5,server_file_md5))

        """[发送] 文件MD5效验对比结果,"""
        if client_file_md5 == server_file_md5:
            md5_send_data = 'success'
            client.send(md5_send_data.encode('utf-8'))
            print('文件下载成功...')
        else:
            md5_send_data = 'error'
            client.send(md5_send_data.encode('utf-8'))
            print('文件出现损坏...请重新下载...')
            """如果失败重新走一遍上面流程"""

    else:
        print('error command')
        continue


client.close()
socket_ftp_client.py
import socket,os,hashlib

"""socket服务器设置"""
server = socket.socket()
server.bind(('127.0.0.1',8999))
server.listen()


while True:

    print('服务器等待请求...')

    conn,addr = server.accept()
    while True:
        """处理请求"""

        """[接收] 文件数据"""
        client_data = conn.recv(1024).decode()

        if len(client_data) == 0 or ' ' not in client_data or not client_data.split(' ')[1]:
            conn.send('error file not found or command error'.encode('utf'))
            continue

        if client_data.startswith('get'):
            file_name = client_data.split(' ')[1]

            print('file_name',file_name)

            """检测文件是否存在"""
            fileexist = os.path.isfile(file_name)
            if not fileexist:
                conn.send('error file not found'.encode('utf-8'))
                continue
            else:

                """[发送] 文件大小"""
                file_size = os.stat(file_name).st_size
                conn.send(str(file_size).encode('utf-8'))

                """[接收] 客户端确认接收数据信息"""
                be_ready_info = conn.recv(1024).decode() #wait for ack
                if be_ready_info != 'be_ready':
                    continue

                m = hashlib.md5()
                try:
                    """[发送] 文件数据"""
                    f = open(file_name, 'rb')
                    for line in f:
                        m.update(line) # 合并文件用于生成MD5效验数据
                        conn.send(line) # 发送文件数据
                    f.close()

                    """[发送] 文件MD5效验值,这里可能会出现粘包的情况,所以在client中接收数据时,做了一些判断"""
                    conn.send(m.hexdigest().encode('utf-8'))

                    """[接收] 文件MD5效验值对比结果"""
                    response_md5_value = conn.recv(1024).decode() # wait for ack
                    print(response_md5_value)
                    # please your action
                    break

                except Exception as e:
                    conn.send('error file open found'.encode('utf-8'))
                    continue
        else:
            conn.send('error command'.encode('utf-8'))
            continue
socket_ftp_server.py

 

posted @ 2018-05-18 15:39  豆腐不怕卤水的经历  阅读(178)  评论(0编辑  收藏  举报