Heim > Backend-Entwicklung > Python-Tutorial > Eine vorläufige Untersuchung der Python-Socket-Programmierung

Eine vorläufige Untersuchung der Python-Socket-Programmierung

高洛峰
Freigeben: 2016-11-21 16:29:00
Original
1252 Leute haben es durchsucht

Socket-Programmierschritte

Der Server erstellt einen Socket, bindet die Adresse und den Port und wartet dann auf eingehende Verbindungen am Port. Sobald eine Verbindung eingeht, empfängt er die eingehende Verbindung über die Akzeptanzfunktion.

Der Client erstellt auch einen Socket. Binden Sie die Remote-Adresse und den Port, stellen Sie dann eine Verbindung her und senden Sie Daten.

Serverseitiger Socket

Das Folgende ist eine detaillierte Beschreibung des serverseitigen socker_server.py anhand eines Beispielcodes

import socket
import sys

HOST = "127.0.0.1"               
PORT = 10000              
s = None
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC,
                              socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
    af, socktype, proto, canonname, sa = res
    try:
        s = socket.socket(af, socktype, proto)
    except socket.error as msg:
        s = None
        continue
    try:
        s.bind(sa)
        s.listen(5)
    except socket.error as msg:
        s.close()
        s = None
        continue
    break
if s is None:
    print 'could not open socket'
    sys.exit(1)
conn, addr = s.accept()
print 'Connected by', addr
while 1:
    data = conn.recv(1024)
    if not data: break
    conn.send(data)
conn.close()
Nach dem Login kopieren

Zuerst verwenden wir den Socket .getaddrinnfo-Funktion zum Konvertieren von Host/Port in eine Sequenz mit 5 Tupeln. Dieses 5-Tupel enthält alle notwendigen Parameter, die wir zum Erstellen einer Socket-Verbindung benötigen. Die zurückgegebenen 5 Tupel sind (family, sockettype, proto, canonname, sockaddr)

Familienadresscluster, der als erster Parameter der socket()-Funktion verwendet wird. Es gibt hauptsächlich die folgenden:

socket.AF_UNIX wird für die Kommunikation mit Prozessen auf einer einzelnen Maschine verwendet.
socket.AF_INET wird für die Kommunikation mit Servern verwendet.
socket.AF_INET6 unterstützt IPv6
sockettype-Socket-Typ, der als zweiter Parameter der socket()-Funktion verwendet wird. Häufig werden

socket.SOCK_STREAM-Standard verwendet, der für das TCP-Protokoll
socket verwendet wird . SOCK_DGRAM wird für das UDP-Protokoll
Protoprotokoll verwendet und als dritter Parameter der socket()-Funktion verwendet. Die getaddrinnfo-Funktion gibt den entsprechenden Protokoll-

Kanonnamen und einen standardisierten Hostnamen basierend auf dem Adressformat und dem Socket-Typ zurück.

sockaddr beschreibt eine Socket-Adresse. Es handelt sich um ein Tupel, das hauptsächlich für die Funktionen bind() und connect() verwendet wird.

Als nächstes erstellen Sie ein Socket-Objekt und übergeben das von der getaddrinnfo-Funktion zurückgegebene af ,Sockettyp,Proto.

s = socket.socket(af, socktype, proto)
Nach dem Login kopieren

Dann binde meine Socket-Adresse

s.bind(sa)
Nach dem Login kopieren

Abhörmodus aktivieren

s.listen(5)
Nach dem Login kopieren

Die Abhörfunktion überwacht die mit dem Socket verbundene Verbindung und die Parameter Geben Sie an, dass das System maximal 5 Verbindungswarteschlangen warten kann, bevor es die Verbindung ablehnt. Diese Verbindungen wurden noch nicht akzeptiert. Die Menge kann nicht unendlich sein, normalerweise wird 5 angegeben.

Sobald wir die Verbindung abhören, rufen wir die Accept-Funktion auf, um die Verbindung zu empfangen.

conn, addr = s.accept()
Nach dem Login kopieren

Die Accept-Funktion gibt ein Tupel zurück, und conn ist ein neues Socket-Objekt, das zum Empfangen und Empfangen verwendet wird Daten senden. addr stellt die Socket-Adresse des anderen Endes dar.

Als nächstes können wir das Conn-Objekt zum Senden und Empfangen von Daten verwenden.

 data = conn.recv(1024) # 接收数据, 这里指定一次最多接收的字符数量为1024
 conn.send(data) # 发送数据
Nach dem Login kopieren

Hier erhalten wir einen Verbindungs-Socket, der nicht mehr ausgeführt wird. Wenn wir also die Verbindung schleifen möchten, verwenden Sie den Funktion akzeptieren Setzen Sie es in eine Endlosschleife.

Client-Socket

Die Client-Socket-Programmierung ist relativ einfach. Nachdem Sie über Connect eine Verbindung mit dem Server hergestellt haben, können Sie miteinander kommunizieren. socket_client.py lautet wie folgt

for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM):
    af, socktype, proto, canonname, sa = res
    try:
        s = socket.socket(af, socktype, proto)
    except socket.error as msg:
        s = None
        continue
    try:
        s.connect(sa)
    except socket.error as msg:
        s.close()
        s = None
        continue
    break
if s is None:
    print 'could not open socket'
    sys.exit(1)
s.sendall('Hello, world')
data = s.recv(1024)
s.close()
print 'Received', repr(data)
Nach dem Login kopieren

Das Obige dient hauptsächlich der Socket-Programmierung von TCP-Stream-Daten. Bei UDP-Protokolldaten ist die Verarbeitung etwas anders. Die Verarbeitungsfunktion zum Senden und Empfangen von UDP-Datenpaketen lautet beispielsweise:

socket.sendto(string, flags, address)
socket.recvfrom(bufsize[, flags]) #返回(string, address),string是返回的数据,address是发送方的socket地址
Nach dem Login kopieren

SocketServer-Modul

Zusätzlich zum Socket-Modul stellt die Netzwerkprogrammierung in Python auch das SocketServer-Modul bereit Das Modul führt hauptsächlich das Socket-Modul Encapsulation aus, das das Erstellen, Binden, Verbinden, Empfangen, Senden und Schließen von Socket-Objekten kapselt, was die Programmierung von Netzwerkdiensten erheblich vereinfacht.

Dieses Modul stellt die folgenden 2 Hauptnetzwerkdienstklassen zum Erstellen entsprechender Socket-Streams bereit

TCPServer erstellt einen Socket-Stream des TCP-Protokolls

UDPServer erstellt einen Socket-Stream des UDP-Protokolls

Wir haben das Socket-Stream-Objekt und benötigen außerdem eine Anforderungsverarbeitungsklasse. Das SocketServer-Modul stellt Anforderungsverarbeitungsklassen bereit, einschließlich BaseRequestHandler und seiner abgeleiteten Klassen StreamRequestHandler und DatagramRequestHandler. Erben Sie also einfach eine dieser drei Klassen und überschreiben Sie dann die Handle-Funktion. Diese Funktion wird zur Verarbeitung der empfangenen Anfrage verwendet. Schauen wir uns ein serverseitiges Codebeispiel an

import SocketServer

class MyTCPHandler(SocketServer.StreamRequestHandler):
   """创建请求处理类,重写handle方法。此外也可以重写setup()和finish()来做一些请求处理前和处理后的一些工作"""
    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()
        print "{} wrote:".format(self.client_address[0])
        print self.data
        # just send back the same data, but upper-cased
        self.request.sendall(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 10000

    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)

    # Activate the server; this will keep running until you
    # interrupt the program with Ctrl-C
    # server.shutdown()
    server.serve_forever()  # 一直循环接收请求
    # server.handle_request() # 只处理一次请求就退出
Nach dem Login kopieren

看着是不是代码简单了很多,而且SocketServer模块内部使用了多路复用IO技术,可以实现更好的连接性能。看serve_forever函数的源代码用到了select模块。通过传入socket对象调用select.select()来监听socket对象的文件描述符,一旦发现socket对象就绪,就通知应用程序进行相应的读写操作。源代码如下:

def serve_forever(self, poll_interval=0.5):
        """Handle one request at a time until shutdown.

        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        """
        self.__is_shut_down.clear()
        try:
            while not self.__shutdown_request:
                # XXX: Consider using another file descriptor or
                # connecting to the socket to wake this up instead of
                # polling. Polling reduces our responsiveness to a
                # shutdown request and wastes cpu at all other times.
                r, w, e = _eintr_retry(select.select, [self], [], [],
                                       poll_interval)
                if self in r:
                    self._handle_request_noblock()
        finally:
            self.__shutdown_request = False
            self.__is_shut_down.set()
Nach dem Login kopieren

即使使用了select技术,TCPServer,UDPServer处理请求仍然是同步的,意味着一个请求处理完,才能处理下一个请求。但SocketServer模块提供了另外2个类用来支持异步的模式。

ForkingMixIn 利用多进程实现异步

ThreadingMixIn 利用多线程实现异步

看名字就知道使用了mixin模式。而mixin模式可以通过多继承来实现,所以通过对网络服务类进行多继承的方式就可以实现异步模式

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass
Nach dem Login kopieren

针对ThreadindMixIn,实现异步的原理也就是在内部对每个请求创建一个线程来处理。看源码

def process_request(self, request, client_address):
        """Start a new thread to process the request."""
        t = threading.Thread(target = self.process_request_thread,
                             args = (request, client_address))
        t.daemon = self.daemon_threads
        t.start()
Nach dem Login kopieren

下面提供一个异步模式的示例

import socket
import threading
import SocketServer

class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        data = self.request.recv(1024)
        cur_thread = threading.current_thread()
        response = "{}: {}".format(cur_thread.name, data)
        self.request.sendall(response)

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass

def client(ip, port, message):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((ip, port))
    try:
        sock.sendall(message)
        response = sock.recv(1024)
        print "Received: {}".format(response)
    finally:
        sock.close()

if __name__ == "__main__":
    # Port 0 means to select an arbitrary unused port
    HOST, PORT = "localhost", 0

    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    ip, port = server.server_address

    # Start a thread with the server -- that thread will then start one
    # more thread for each request
    server_thread = threading.Thread(target=server.serve_forever)
    # Exit the server thread when the main thread terminates
    server_thread.daemon = True
    server_thread.start()
    print "Server loop running in thread:", server_thread.name

    client(ip, port, "Hello World 1")
    client(ip, port, "Hello World 2")
    client(ip, port, "Hello World 3")

    server.shutdown()
    server.server_close()
Nach dem Login kopieren

以上是本人对socket相关的理解,有什么不当或错误之处,还请指出。

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage