zoukankan      html  css  js  c++  java
  • 网络编程socket方法

    1、理解socket
        Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,
        让Socket去组织数据,以符合指定的协议。
    2、网络编程事例
    import socket
    
    # 创建服务端socket对象
    server = socket.socket()
    
    # 绑定IP和端口
    server.bind(('192.168.13.155',8000))
    
    # 后边可以等5个人
    server.listen(5)
    
    print('服务端准备开始接收客户端的连接')
    # 等待客户端来连接,如果没人来就傻傻的等待。
    # conn是客户端和服务端连接的对象(伞),服务端以后要通过该对象进行收发数据。
    # addr是客户端的地址信息。
    # #### 阻塞,只有有客户端进行连接,则获取客户端连接然后开始进行通信。
    conn,addr = server.accept()
    
    print('已经有人连接上了,客户端信息:',conn,addr)
    
    # 1024表示:服务端通过(伞)获取数据时,一次性最多拿1024字节。
    data = conn.recv(1024)
    print('已经有人发来消息了',data)
    
    # 服务端通过连接对象(伞)给客户端回复了一个消息。
    conn.send(b'stop')
    
    # 与客户端断开连接(放开那把伞)
    conn.close()
    
    # 关闭服务端的服务
    server.close()
    
    服务器端
    服务端
    import socket
    
    client = socket.socket()
    
    # 向服务端发起连接请求(递伞)
    # 阻塞,去连接,直到连接成功后才会继续向下走。
    client.connect(('192.168.13.155',8000))
    
    # # 链接上服务端后,向服务端发送消息
    client.send(b'hello')
    
    # 等待服务端给他发送消息
    data = client.recv(1024)
    print(data)
    
    # 关闭自己
    client.close()
    客户端

    3、黏包现象

      黏包现象只发生在tcp协议中:

        1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。

        2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

       黏包的解决方案:

          引入struct模块,构建报头数据

    import struct    #将一定长度的数字类型转换成固定长度的四个字节
    
    res=struct.pack("i",12345)
    print(res)      #b'90x00x00'
    print(len(res))  #4
    print(type(res))   #<class 'bytes'>
    
    
    obj=struct.unpack("i",res)
    print(obj)     #(12345,)
    print(obj[0])   #12345
    print(type(obj[0]))    #<class 'int'>
    struct模块的作用

    subprocess模块

    import subprocess      #执行终端命令
    
    res=subprocess.Popen("dir",
                         shell=True,
                         stderr=subprocess.PIPE,
                         stdout=subprocess.PIPE)
    print(res.stdout.read().decode("gbk"))    #执行终端的dir命令,打印当前目录下的文件信息
    server端
    import socket
    import os
    import json
    import struct
    import hashlib
    
    sock=socket.socket()
    sock.connect(("127.0.0.1",8800))
    while 1 :
        cmd=input("请输入命令:") # put 111.jpg
    
        action,filename=cmd.strip().split(" ")
        filesize=os.path.getsize(filename)
    
        file_info={
            "action": action,
            "filename": filename,
            "filesize": filesize,
        }
        file_info_json=json.dumps(file_info).encode("utf8")
    
        ret=struct.pack("i",len(file_info_json))
        # 发送 file_info_json的打包长度
        sock.send(ret)
        # 发送 file_info_json字节串
        sock.send(file_info_json)
        # 发送 文件数据
        md5=hashlib.md5()
        with open(filename,"rb") as f:
            for line in f:
                sock.send(line)
                md5.update(line)
    
        data=sock.recv(1024)
        print(md5.hexdigest())
        md5_val=md5.hexdigest()
        sock.send(md5_val.encode("utf8"))
        is_valid=sock.recv(1024).decode('utf8')
        if is_valid=="203":
            print("文件完整!")
        else:
            print("文件上传失败!")
    client端

    socket的更多方法介绍

    服务端套接字函数
    s.bind()    绑定(主机,端口号)到套接字
    s.listen()  开始TCP监听
    s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来
    
    客户端套接字函数
    s.connect()     主动初始化TCP服务器连接
    s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
    
    公共用途的套接字函数
    s.recv()            接收TCP数据
    s.send()            发送TCP数据
    s.sendall()         发送TCP数据
    s.recvfrom()        接收UDP数据
    s.sendto()          发送UDP数据
    s.getpeername()     连接到当前套接字的远端的地址
    s.getsockname()     当前套接字的地址
    s.getsockopt()      返回指定套接字的参数
    s.setsockopt()      设置指定套接字的参数
    s.close()           关闭套接字
    
    面向锁的套接字方法
    s.setblocking()     设置套接字的阻塞与非阻塞模式
    s.settimeout()      设置阻塞套接字操作的超时时间
    s.gettimeout()      得到阻塞套接字操作的超时时间
    
    面向文件的套接字的函数
    s.fileno()          套接字的文件描述符
    s.makefile()        创建一个与该套接字相关的文件
    更多方法
    官方文档对socket模块下的socket.send()和socket.sendall()解释如下:
    
    socket.send(string[, flags])
    Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Returns the number of bytes sent. Applications are responsible for checking that all data has been sent; if only some of the data was transmitted, the application needs to attempt delivery of the remaining data.
    
    send()的返回值是发送的字节数量,这个数量值可能小于要发送的string的字节数,也就是说可能无法发送string中所有的数据。如果有错误则会抛出异常。
    
    –
    
    socket.sendall(string[, flags])
    Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Unlike send(), this method continues to send data from string until either all data has been sent or an error occurs. None is returned on success. On error, an exception is raised, and there is no way to determine how much data, if any, was successfully sent.
    
    尝试发送string的所有数据,成功则返回None,失败则抛出异常。
    
    故,下面两段代码是等价的:
    
    #sock.sendall('Hello world
    ')
    
    #buffer = 'Hello world
    '
    #while buffer:
    #    bytes = sock.send(buffer)
    #    buffer = buffer[bytes:]
    send和sendall方法

    socket.getaddrinfo(hostportfamily=0type=0proto=0flags=0) #获取要连接的对端主机地址

    sk.bind(address)

      s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

    sk.listen(backlog)

      开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。

          backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
          这个值不能无限大,因为要在内核中维护连接队列

    sk.setblocking(bool)

      是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。

    sk.accept()

      接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。

      接收TCP 客户的连接(阻塞式)等待连接的到来

    sk.connect(address)

      连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

    sk.connect_ex(address)

      同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061

    sk.close()

      关闭套接字

    sk.recv(bufsize[,flag])

      接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

    sk.recvfrom(bufsize[.flag])

      与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

    sk.send(string[,flag])

      将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。

    sk.sendall(string[,flag])

      将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

          内部通过递归调用send,将所有内容发送出去。

    sk.sendto(string[,flag],address)

      将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

    sk.settimeout(timeout)

      设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

    sk.getpeername()

      返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

    sk.getsockname()

      返回套接字自己的地址。通常是一个元组(ipaddr,port)

    sk.fileno()

      套接字的文件描述符

    socket.sendfile(fileoffset=0count=None)

         发送文件 ,但目前多数情况下并无什么卵用。

  • 相关阅读:
    pugixml
    C++ 头文件的理解
    图像的特征
    光圈与景深
    Unix高级环境编程
    用libtommath实现RSA算法
    【linux+C】神器 vim + 指针相关客串
    【算法25】对称子字符串的最大长度
    设计并实现同时支持多种视频格式的流媒体点播系统
    递归再一次让哥震惊了
  • 原文地址:https://www.cnblogs.com/shanae/p/9592458.html
Copyright © 2011-2022 走看看