zoukankan      html  css  js  c++  java
  • python socket编程

    一.socket定义

    socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭),基本上Socket 是任何一种计算机网络通讯中最基础的内容。例如当你在浏览器地址栏中输入 http://www.cnblogs.com/ 时,你会打开一个套接字,然后连接到 http://www.cnblogs.com/ 并读取响应的页面然后然后显示出来。而其他一些聊天客户端如 gtalk 和 skype 也是类似。任何网络通讯都是通过 Socket 来完成的。socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递。 我们知道网络 通信 都 是基于 ip+port 方能定位到目标的具体机器上的具体服务,操作系统有0-65535个端口,每个端口都可以独立对外提供服务,如果 把一个公司比做一台电脑 ,那公司的总机号码就相当于ip地址, 每个员工的分机号就相当于端口, 你想找公司某个人,必须 先打电话到总机,然后再转分机 。

    socket和file的区别:

      1、file模块是针对某个指定文件进行【打开】【读写】【关闭】

      2、socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】

    python提供了两个基本的socket模块:

          1.socket提供了标准的BSD Socket API

      2.SocketSverver提供了服务器的重心,可以简化网络服务器的开发

    Python 官方关于 Socket 的函数请看 http://docs.python.org/library/socket.html

    二.socket类型

    套接字格式:socket(family,type[,protocal])使用给定的套接族,套接字类型,协议编号(默认为0)来创建套接字

    socket.AF_UNIX:用于同一台机器上的进程通信(即本机通信)

    socket.AF_INET:用于服务器和服务器之间的网络通信

    socket.AF_INET6:基于IPV6方式的服务器和服务器之间的网络通信

    socket.SOCK_STREAM:基于TCP式的socket通信

    socket.SOCK_DGRAM:基于UDP的数据报式socket通信

    socket.SOCK_RAM:原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次SOCK_RAW也可以处理特殊的IPV4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头

    socket.SOCK_SEQPACKET:可靠的连续数据包服务

     三.Socket函数

    • TCP发送数据时,已建立好TCP链接,所以不需要指定地址,而UDP是面向无连接的,每次发送都需要指定发送给谁。
    • 服务器与客户端不能直接发送列表,元素,字典等带有数据类型的格式,发送的内容必须是字符串数据。

    服务器端 Socket 函数

    s.bind(address):将套接字绑定到地址,在AF_INET下,以tuple(host,port)方式传入,如s.bind((host,port))

    s.listin(backlog):开始监听TCP传入连接,backlog指定在拒绝链接前,操作系统可以挂起的最大连接数,该值最少为大部分应用程序设为5就够用了

    s.accept():接收TCP链接并返回(conn,address),其中conn是新的套接字对象,可以用来发送和接收数据,address是链接客户端的地址

     客户端Socket函数

    s.connect(address):链接到address处的套接字,一般address的格式为tuple(host,port),如果链接出错,则返回socket.error错误

    s.connect_ex(address):功能与s.connect(address)相同,但成功返回0,失败返回error的值

    公共 Socket 函数

    s.recv(bufsize[, flag]):接受TCP套接字的数据,数据以字符串形式返回,buffsize指定要接受的最大数据量,flag提供有关消息的其他信息,通常可以忽略

     s.send(string[, flag]):发送TCP数据,将字符串中的数据发送到链接的套接字,返回值是要发送的字节数量,该数量可能小于string的字节大小

    s.sendall(string[, flag]):完整发送TCP数据,将字符串中的数据发送到链接的套接字,但在返回之前尝试发送所有数据。成功返回None,失败则抛出异常

    s.recvfrom(bufsize[, flag]):接受UDP套接字的数据u,与recv()类似,但返回值是tuple(data, address)。其中data是包含接受数据的字符串,address是发送数据的套接字地址

    s.sendto(string[, flag], address):发送UDP数据,将数据发送到套接字,address形式为tuple(ipaddr, port),指定远程地址发送,返回值是发送的字节数

    s.close():关闭套接字

    s.getpeername():返回套接字的远程地址,返回值通常是一个tuple(ipaddr, port)

    s.getsockname():返回套接字自己的地址,返回值通常是一个tuple(ipaddr, port)

    s.setsockopt(level, optname, value):设置给定套接字选项的值

    s.getsockopt(level, optname[, buflen]):返回套接字选项的值

    s.settimeout(timeout):设置套接字操作的超时时间,timeout是一个浮点数,单位是秒,值为None则表示永远不会超时。一般超时期应在刚创建套接字时设置,因为他们可能用于连接的操作,如s.connect()

    s.gettimeout():返回当前超时值,单位是秒,如果没有设置超时则返回None

    s.fileno():返回套接字的文件描述

    s.setblocking(flag):如果flag为0,则将套接字设置为非阻塞模式,否则将套接字设置为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。

    s.makefile():创建一个与套接字相关的文件

    四.Socket编程实例

    建立一个socket必须至少有2端, 一个服务端,一个客户端, 服务端被动等待并接收请求,客户端主动发起请求, 连接建立之后,双方可以互发数据。

    jian见到那


    客户端和服务端相互通信:

    #!/usr/bin/python3
    import socket
    HOST = 'localhost'
    PORT = 8001
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.bind((HOST, PORT))
    s.listen(5)
    print('Server start at: %s:%s' %(HOST, PORT))
    print('wait for connection...')
    while True:
        conn, addr = s.accept()
        print('Connected by ',addr)
        while True:
            data = conn.recv(1024)
            print(data)
            conn.send(bytes("The server successfully receives your information.",encoding='utf-8')
    服务端代码
    #!/usr/bin/env python3
    import socket
    HOST = 'localhost'
    PORT = 8001
    
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    
    while True:
        cmd = input("Please input msg:")
        s.send(bytes(cmd,encoding='utf-8'))
        data = s.recv(1024)
        print(data)
        if cmd == 'q':break
    客户端代码

     通过socket实现简单的ssh:

     1 import socket,os,time
     2 server=socket.socket()
     3 server.bind(('localhost',9999))
     4 server.listen()
     5 while True:
     6     conn,addr = server.accept()
     7     print("now conn",conn,addr)
     8     while True:
     9         print("等待新指令")
    10         data = conn.recv(1024)
    11         if not data:
    12             print("客户端已断开")
    13             break
    14         print("执行指令",data)
    15         cmd_res = os.popen(data.decode()).read() #接受和执行结果都是字符串
    16         if len(cmd_res) ==0:
    17             cmd_res="cmd has no output"
    18         conn.send(str(len(cmd_res.encode())).encode("utf-8"))  #注意中文解码成bytes再len统计,否则出现误差
    19         #time.sleep(0.5) #此方法可以解决粘包问题,这种方法比较low
    20         client_ack = conn.recv(1024) #接受客户端的回复,解决粘包
    21         print("client_ack",client_ack.decode())
    22         conn.send(cmd_res.encode("utf-8"))
    23         print("send done")
    24 server.close()
    服务端代码
     1 import socket
     2 client = socket.socket()
     3 client.connect(('localhost',9999))
     4 while True:
     5     cmd = input(">>>").strip()
     6     if len(cmd) == 0:continue
     7     client.send(cmd.encode('utf-8'))
     8     cmd_res_size = client.recv(1024)#接受命令结果的长度
     9     print("命令结果大小",cmd_res_size)
    10     client.send("准备好接收,可以发了".encode('utf-8')) #给服务端发一条回复解决粘包问题
    11     received_size = 0
    12     received_data = b''
    13     while received_size != int(cmd_res_size.decode()):
    14         data = client.recv(1024)
    15         received_size += len(data) #接受到的可能小于1024,用len判断
    16         received_data += data
    17     else:
    18         print("cmd_res receive done",received_size)
    19         print(received_data.decode())
    20 client.close()
    客户端代码

    通过socket实现文件下载:

     1 import socket,os,time,hashlib
     2 server=socket.socket()
     3 server.bind(('localhost',9999))
     4 server.listen()
     5 while True:
     6     conn,addr = server.accept()
     7     print("now conn",conn,addr)
     8     while True:
     9         print("等待新指令")
    10         data = conn.recv(1024)
    11         if not data:
    12             print("客户端已断开")
    13             break
    14         cmd,filename = data.decode().split()
    15         print(filename)
    16         if os.path.isfile(filename):
    17             f = open(filename,"rb")
    18             m = hashlib.md5()
    19             file_size = os.stat(filename).st_size
    20             conn.send(str(file_size).encode())#发送文件大小
    21             conn.recv(1024) #wait for ack
    22             for line in f:
    23                 m.update(line)
    24                 conn.send(line)
    25             print("file md5",m.hexdigest())
    26             f.close()
    27             conn.send(m.hexdigest().encode()) #send md5
    28         print("send done")
    29 server.close()
    服务端代码
     1 import socket,hashlib
     2 client = socket.socket()
     3 client.connect(('localhost',9999))
     4 while True:
     5    cmd = input(">>>").strip()
     6    if len(cmd) == 0:continue
     7    if cmd.startswith("get"):
     8        client.send(cmd.encode())
     9        server_response = client.recv(1024)
    10        print("server response",server_response)
    11        client.send(b"ready to recv file")
    12        file_total_size = int(server_response.decode())
    13        received_size = 0
    14        filename = cmd.split()[1]
    15        f = open(filename + "new","wb")
    16        m = hashlib.md5()
    17        while received_size < file_total_size:
    18            #判断是否是最后一次接受数据,主要解决粘包问题
    19            if file_total_size - received_size > 1024: #要接受不止一次
    20                size = 1024
    21            else: #最后一次,有多少收多少
    22                size = file_total_size - received_size
    23                print("last receive",size)
    24            data = client.recv(size)
    25            received_size += len(data)
    26            m.update(data)
    27            f.write(data)
    28            #print(file_total_size,received_size)
    29        else:
    30            new_file_md5 = m.hexdigest()
    31            print("file recv done",file_total_size,received_size)
    32            f.close()
    33            service_file_md5 = client.recv(1024)
    34            print("server file md5",service_file_md5)
    35            print("client file md5",new_file_md5)
    客户端代码

    五.SocketServer

    The socketserver module simplifies the task of writing network servers.

    socketserver一共有这么几种类型:

    This uses the Internet TCP protocol, which provides for continuous streams of data between the client and server.

    class socketserver.TCPServer(server_address, RequestHandlerClass,bind_and_activate=True) 
    

     This uses datagrams, which are discrete packets of information that may arrive out of order or be lost while in transit. The parameters are the same as for TCPServer.

    class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)
    

     These more infrequently used classes are similar to the TCP and UDP classes, but use Unix domain sockets; they’re not available on non-Unix platforms. The parameters are the same as for TCPServer.

    class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
    class socketserver.UnixDatagramServer(server_address, RequestHandlerClass,bind_and_activate=True)
    

     There are five classes in an inheritance diagram, four of which represent synchronous servers of four types:

    +------------+
    | BaseServer |
    +------------+
          |
          v
    +-----------+        +------------------+
    | TCPServer |------->| UnixStreamServer |
    +-----------+        +------------------+
          |
          v
    +-----------+        +--------------------+
    | UDPServer |------->| UnixDatagramServer |
    +-----------+        +--------------------+


    创建一个socketserver 至少分以下几步:

    1. First, you must create a request handler class by subclassing the BaseRequestHandlerclass and overriding its handle() method; this method will process incoming requests.   
    2. Second, you must instantiate one of the server classes, passing it the server’s address and the request handler class.
    3. Then call the handle_request() orserve_forever() method of the server object to process one or many requests.
    4. Finally, callserver_close() to close the socket.
       1 import socket
       2 client = socket.socket()
       3 client.connect(('localhost',9999))
       4 while True:
       5     cmd = input(">>>:").strip()
       6     if len(cmd) == 0:continue
       7     client.send(cmd.encode('utf-8'))
       8     data = client.recv(10240)
       9     print("recv",data.decode())
      10 client.clos

    基本socketserver简单代码:

     1 import socketserver
     2 class MyTCPHandle(socketserver.BaseRequestHandler):
     3     def handle(self): #接受处理客户端的请求
     4         while True:
     5             try:
     6                 self.data = self.request.recv(1024).strip()
     7                 print("{} wrote".format(self.client_address[0]))
     8                 print(self.data)
     9                 self.request.send(self.data.upper())
    10             except ConnectionResetError as e:
    11                 print("err",e)
    12                 break
    13 
    14 if __name__ == "__main__":
    15     HOST,PORT = "localhost",9999
    16     server = socketserver.TCPServer((HOST,PORT),MyTCPHandle)
    17     server.serve_forever()
    服务端代码
     1 import socket
     2 client = socket.socket()
     3 client.connect(('localhost',9999))
     4 while True:
     5     cmd = input(">>>:").strip()
     6     if len(cmd) == 0:continue
     7     client.send(cmd.encode('utf-8'))
     8     data = client.recv(10240)
     9     print("recv",data.decode())
    10 client.close()
    客户端代码

     实现多并发:客户端每连进一个来,服务器端就会分配一个新的线程来处理这个客户端的请求。

    server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)   -----》  改为:
    server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
    server = socketserver.ForkingTCPServer((HOST,PORT),MyTCPHandle)  主要在linux系统上。
  • 相关阅读:
    opencart后台操作--第一节 多语言篇---中文语言包
    Apache mod_rewrite实现HTTP和HTTPS重定向跳转
    js搞定网页的简繁转换
    TP5 首页导航一级和二级分类
    php中is_null,empty,isset,unset 的区别详细介绍
    thinkPHP5 引入模板
    mac Gitblit安装
    springCloud 之 Eureka注册中心高可用配置
    spring cloud config-配置中心
    链路追踪工具Zipkin简单整合
  • 原文地址:https://www.cnblogs.com/wenwei-blog/p/7655888.html
Copyright © 2011-2022 走看看