第八节

python3 socket编程

Socket(翻译为套接字, 我觉得很挫),是操作系统内核中的一个数据结构,它是网络中的节点进行相互通信的门户。它是网络进程的ID。网络通信,归根到底还是进程间的通信(不同计算机上的进程间通信, 又称进程间通信, IP协议进行的主要是端到端通信)。在网络中,每一个节点(计算机或路由)都有一个网络地址,也就是IP地址。两个进程通信时,首先要确定各自所在的网络节点的网络地址。但是,网络地址只能确定进程所在的计算机,而一台计算机上很可能同时运行着多个进程,所以仅凭网络地址还不能确定到底是和网络中的哪一个进程进行通信,因此套接口中还需要包括其他的信息,也就是端口号(PORT)。在一台计算机中,一个端口号一次只能分配给一个进程,也就是说,在一台计算机中,端口号和进程之间是一一对应关系。
所以,使用端口号和网络地址的组合可以唯一的确定整个网络中的一个网络进程.
每一个socket都用一个半相关描述{协议、本地地址、本地端口}来表示;一个完整的套接字则用一个相关描述{协议、本地地址、本地端口、远程地址、远程端口}来表示。socket也有一个类似于打开文件的函数调用,该函数返回一个整型的socket描述符,随后的连接建立、数据传输等操作都是通过socket来实现的

流程描述socket 

  tcp/ip send,recv

  udp

family address

  AF.NET  ipv4

  AF.INET6

  AF.UNIX  local

socket protocol type

  SOCK_STREAM tcp/ip

  SOCK_DGREAM 数据报式socket,udp

server = socket.socker(AF_INET,SOCK_STREAM)

server.bind(ip_address,port)

server.listen()

while True:

  conn,addr = server.accept()  #开始堵塞

  while True:

    print("new conn")

    date = conn.recv(1024)  #官方建议不超过8192K

    if not data:break

    print(data)

    conn.send(data.upper())
服务端
client = socket.socket()

client.connect(("server_ip",port))

client.send((data))

client.recv(data)
客户端
TCP通信的基本步骤如下:
服务端:socket---bind---listen---while(True){---accept---recv---send----}---close
客户端:socket------------------------------------connect---send---recv-------close

socket函数

server = socket.socket()

server.recv() 
server.send()
server.close()
server.close()
server.listen()
server.bind()
server.type()
server.accept()  #等待一个进来的连接,返回一个表示连接的新socket和客户端ip。对于ip docket来说,地址信息是一对(hostaddr,port)
server.connect_ex()
server.detath()
server.dupv()
server.family()
server.fileno()
server.get_inheritable()
server.getpeername()
server.getsockname()
server.gettimeout()
server.makefile()  #
server.recv_into()
server.recvfrom()
server.sendall()
server.sendto()
server.sendfile(self, file, offset=0, count=None)  #通过使用高性能的os.sendfile()发送一个文件,并返回发送的字节总数。文件必须是一个标准的二进制打开文件,如果文件不可达,或者文件不是标准的文件,socket.send()会被使用。
server.settimeout()
server.shutdown()
server.timeout()

 

 利用socket实现简单的ssh服务器与客户端

import socket,os

server =  socket.socket()
server.bind(("localhost",9999))

server.listen()

while True:
    conn,addr = server.accept()
    print("new conn:",addr)
    while True:
        print("等待新指令")
        data = conn.recv(1024)
        if not data:
            print("客户端已断开")
            break
        print("执行新指令:",data)
        cmd_res = os.popen(data.decode()).read()
        print("before send:",len(cmd_res))
        if len(cmd_res) == 0:
            cmd_res = "cmd has no output..."
        conn.send(str(len(cmd_res.encode())).encode())
        cilent_ack = conn.recv(1024)
        print("cilent has already receive....")
        conn.send(cmd_res.encode())
        print("send done")

server.close()
socket ssh server
import socket,os
client = socket.socket()

client.connect(("localhost",9999))

while True:
    cmd = input(">>:").strip()
    if len(cmd) == 0: continue
    client.send(cmd.encode("utf-8"))
    cmd_res_size = client.recv(1024)
    client.send("可以发送数据了".encode())
    received_size = 0
    received_data = b""
    while received_size < int(cmd_res_size.decode()):
        data = client.recv(1024)
        received_size += len(data)
        received_data += data
    else:
        print("cmd res receive done...",received_size)
        print(received_data.decode())

client.close()
socket ssh client

 利用socket实现简单的ftp文件下载

import socket,os,hashlib

server =  socket.socket()
server.bind(("localhost",9999))

server.listen()

while True:
    conn,addr = server.accept()
    print("new conn:",addr)
    while True:
        print("等待新指令")
        data = conn.recv(1024)
        if not data:
            print("客户端已断开")
            break

        cmd,filename = data.encode().split()
        print(filename)
        if  os.path.isfile(filename):
            f = open(filename,"rb")
            m = hashlib.md5()
            file_size = os.stat(filename).st_size
            conn.send(str(file_size).encode())
            conn.recv(1024)
            for line in f:
                m.update(line)
                conn.send(line)
            print("file md5",m.hexdigest())
            f.close()
            conn.send(m.hexdigest().encode())

        print("send done")

server.close()
ftp server
import socket,os,hashlib
client = socket.socket()

client.connect(("localhost",9999))

while True:
    cmd = input(">>:").strip()
    if len(cmd) == 0: continue
    if cmd.startswith("get"):
        client.send(cmd.encode())
        server_respone = client.recv(1024)
        print("server respone:",server_respone)
        client.send(b"ready to recv file")
        file_total_size = len(server_respone.decode())
        received_size = 0
        filename = cmd.split()[1]
        f = open(filename +".new","wb")
        m = hashlib.md5()
        while received_size < file_total_size:
            if file_total_size - received_size > 1024:
                size = 1024
            else:
                size = file_total_size - received_size
                print("last received size",size)
            data = client.recv(size)
            received_size += len(data)
            m.update(data)
            f.write(data)
        else:
            new_file_md5 = m.hexdigest()
            print("file rece done",received_size,file_total_size)
            f.close()

        server_file_md5 = client.recv(1024)
        print("server file md5:",server_file_md5)

client.close()
ftp client

 sockerserver介绍


The classes in this module favor the server type that is simplest to
write: a synchronous TCP/IP server. This is bad class design, but
save some typing. (There's also the issue that a deep class hierarchy
slows down method lookups.)

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

+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
Note that UnixDatagramServer derives from UDPServer, not from
UnixStreamServer -- the only difference between an IP and a Unix
stream server is the address family, which is simply repeated in both
unix server classes.

Forking and threading versions of each type of server can be created
using the ForkingMixIn and ThreadingMixIn mix-in classes. For
instance, a threading UDP server class is created as follows:

class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass

The Mix-in class must come first, since it overrides a method defined
in UDPServer! Setting the various member variables also changes
the behavior of the underlying server mechanism.

To implement a service, you must derive a class from
BaseRequestHandler and redefine its handle() method. You can then run
various versions of the service by combining one of the server classes
with your request handler class.

The request handler class must be different for datagram or stream
services. This can be hidden by using the request handler
subclasses StreamRequestHandler or DatagramRequestHandler.

Of course, you still have to use your head!

For instance, it makes no sense to use a forking server if the service
contains state in memory that can be modified by requests (since the
modifications in the child process would never reach the initial state
kept in the parent process and passed to each child). In this case,
you can use a threading server, but you will probably have to use
locks to avoid two requests that come in nearly simultaneous to apply
conflicting changes to the server state.

On the other hand, if you are building e.g. an HTTP server, where all
data is stored externally (e.g. in the file system), a synchronous
class will essentially render the service "deaf" while one request is
being handled -- which may be for a very long time if a client is slow
to read all the data it has requested. Here a threading or forking
server is appropriate.

In some cases, it may be appropriate to process part of a request
synchronously, but to finish processing in a forked child depending on
the request data. This can be implemented by using a synchronous
server and doing an explicit fork in the request handler class
handle() method.

Another approach to handling multiple simultaneous requests in an
environment that supports neither threads nor fork (or where these are
too expensive or inappropriate for the service) is to maintain an
explicit table of partially finished requests and to use a selector to
decide which request to work on next (or whether to handle a new
incoming request). This is particularly important for stream services
where each client can potentially be connected for a long time (if
threads or subprocesses cannot be used).

Future work:
- Standard classes for Sun RPC (which uses either UDP or TCP)
- Standard mix-in classes to implement various authentication
and encryption schemes

XXX Open problems:
- What to do with out-of-band data?

BaseServer:
- split generic "request" functionality out into BaseServer class.
Copyright (C) 2000 Luke Kenneth Casson Leighton <lkcl@samba.org>

example: read entries from a SQL database (requires overriding
get_request() to return a table entry from the database).
entry is processed by a RequestHandlerClass.

 handle方法

class BaseRequestHandler:

    """Base class for request handler classes.

    This class is instantiated for each request to be handled.  The
    constructor sets the instance variables request, client_address
    and server, and then calls the handle() method.  To implement a
    specific service, all you need to do is to derive a class which
    defines a handle() method.

    The handle() method can find the request as self.request, the
    client address as self.client_address, and the server (in case it
    needs access to per-server information) as self.server.  Since a
    separate instance is created for each request, the handle() method
    can define other arbitrary instance variables.

    """

    def __init__(self, request, client_address, server):
        self.request = request
        self.client_address = client_address
        self.server = server
        self.setup()
        try:
            self.handle()
        finally:
            self.finish()

    def setup(self):
        pass

    def handle(self):
        pass

    def finish(self):
        pass


# The following two classes make it possible to use the same service
# class for stream or datagram servers.
# Each class sets up these instance variables:
# - rfile: a file object from which receives the request is read
# - wfile: a file object to which the reply is written
# When the handle() method returns, wfile is flushed properly

一个简单的socketserver

import socketserver

class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            try:
                self.data = self.request.recv(1024).strip()
                print("{} wrote:".format(self.client_address[0]))
                print(self.data)
                self.request.send(self.data.upper())
            except ConnectionResetError as e:
                print("err:",e)
                break

if __name__ == '__main__':
    HOST,PORT = "localhost",9999
    server = socketserver.TCPServer((HOST,PORT),MyTCPHandler)
    server.serve_forever()
socketserver

 

 

  

 

posted @ 2017-08-22 23:08  酷酷的狐狸  阅读(206)  评论(0编辑  收藏  举报