1. 程式人生 > >python網路程式設計:TCP/IP、Socket、C/S架構等

python網路程式設計:TCP/IP、Socket、C/S架構等

網路程式設計

網路程式設計

tcp/ip

套接字

  • Socket來源

    • 通訊的基石,是支援TCP/IP協議的網路通訊的基本操作單元
    • 屬性:三元組(ip地址, 協議,埠)
      • 型別
      • 協議
    • IP地址
    • IP地址是一個32位的二進位制數就是4個位元組

    socket

通過指定的埠和協議找到伺服器名

import socket

def find_service_name():
    protocolname = 'tcp'
    for port in [80, 25]:
        print "Port: %s => service naem: %s" % (port, socket.getservbyport(port,portocolname)
    print "Port: %s => service name: %s" % (53, socket.getservbyport(53, 'ndp'))

find_service_name()

IP地址上的轉換

import socket
from binascii import hcxlify

def convert_ipv4_addrss):
    for ip_addr in ['127.0.0.1', '192.168.0.1']:
        packed_ip_addr = socket.inet_aton(ip_addr)
        unpacked_ip_addr = socket.inet_ntoa(packed_ip_addr)
        print "IP address: %s -> packed: %s, unpacked:%s" % (ip_addr, hexlify(packed_ip_addr), unpacked_ip_addr)

ntp

授時協議

import ntplib
from time import ctime

def print_time():
    ntp_client = ntplib.NTPClient()
    response = ntp_client.request('time.nist.gov')
    print ctime(response.tx_time)

print_time()

C/S結構與套接字

  • 服務端socket(六個步驟)

    • 建立socket物件,呼叫socket建構函式:socket.socket()
    • 將socket繫結到指定地址上,socket.bind()
    • 監聽,準備好套接字,以便接受連結請求:socket.listen()
    • 等待客戶請求一個連結:socket.accept()
    • 處理階段,伺服器與客戶端通過send和recv方法通訊(傳輸資料)
    • 傳輸結束,伺服器呼叫socked的close方法以關閉連線
  • 客戶端socket(四個步驟)

    • 建立一個socket以連線伺服器
    • connect方法連線伺服器
    • 客戶端和伺服器通過send和recv方法通訊
    • 客戶端通過呼叫socket的close方法關閉連線

圖解socket

  • 簡單的Socket C/S
    • Echo Server /Client
    • 基於TCP
    • Server回顯Client傳送資訊
    • Server停止當傳送“Bye”的時候

迴響

Server端

import socket
import sys
host = 'localhost'
backlog = 50
def echo_server(port):
    """ simple echo server"""
    # Create a TCP socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # Enable reuse address/port
    sock = socket.setsockopt(socket.SOL_SOCKET, socket.SO_RESUSEADDR,1)
    #Bind the socket to the port
    server_address = (host, port)
    print "Starting up echo server on %s port %s" % server_address
    sock.bind(server_address)
    # Listen to clients, backlog argument specifies the max no. of queued connections
    sock.listen(backlog)

    while True:
        print "Waiting to receive messing from client"
        client, address = sock.accept()
        data = client.recv(2048)
        print "Send %s bytes back to %s" %(data, address)
        if data = 'bye':
            print " Data %s" % data
            client.send(data)
            print "Send %s byte to %s" % (data, address)
            if data == 'bye':
                print "echo server stop"
                break
        client.close()

    echo_server(9999)

Client端

import socket
import sys
import argparge

host = 'localhost'

def echo_client(port):
    """A simple echo client"""
    sock = socket.socket(socket.AF_IENT, socket.SOCK_STREAM)
    server_address = (host, port)
    print "Connecting to %s port %s" % server_address
    sock.connect(server_address)


    try:
        message = "bye"
        print "Sending: %s" % message
        sock.sendall(message)
        amount_received = 0
        amount_expected = len(message)
        while amount_received < amount_expected:
            data = sock.recv(2048)
            amount_received += len(data)
            print "Recived: %s" % data

    except socket.error, e:
        print "Socket error: %s" % str(e)
    except Exception, e:
        print "Other exception: %s" % str(e)
    finally:
        print "Closing connection to the server"
        sock.close()

echo_client(9999)

檔案傳輸

傳輸端

import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('localhost', 9999)
print "Connecting to %s port %s" % server_address
sock.connect(server_address)

with open('data/ft_test_client.txt', 'r') as fp:
    sock.sendall(fp.read())
    print 'sending file'

sever端

import socket
import sys
host = 'localhost'
backlog = 50
def file_server(port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_address = (host, port)
    print "Starting up file server on %s port %s" %server_address
    sock.bind(server_address)
    sock.listen(backlog)
    while True:
        print "Waiting to receive file from client"
        client, address = sock.accept()
        data = client.recv(2048)
        if data:
            print 'received file'
            with open('ft_open_server.txt', 'w') as fp:
                fp.write(data)
            break
        client.close()
    print "server closed"
file_server(9999)

高效能傳輸

ThreadingMixin

server端

from SocketServer import TCPServer, BaseRequestHandler
class MyBaseRequestHandler(BaseRequestHandler):
    """
    從BaseRequestHander繼承,並重寫handle方法
    """
    def handle(self):
        while(self):
            try:
                data = self.request.recv(1024).strip()
                print "receive from (%r):%r" % (self.client_address, data)
                self.requset.sendall(data.upper())
                if data == 'y':
                    print 'server exit'
                    break
            except:
                break

    host = "0.0.0.0"
    port = 9997
    addr = (host, port)
    server = TCPServer(addr, MyBaseRequestHandlerr)
    print 'server is started'
    server.serve_forever()

client端

import os
import socket
import threading
import SocketServer

SERVER_HOST = 'localhost'
SERVER_PORT = 0  # Tell the kernel to pick up a port dynamically
BUF_SIZE = 1024

def client(ip, port, message):
    """A client to test threading mixin server"""
    # Connect to the server
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((ip, port))
    try:
        sock.sendall(message)
        response = sock.recv(BUF_SIZE)
        print "Client recevied: %s" % response
    finally:
        sock.close()

class ThreadTCPRequestHandler(SocketServer.BaseRequestHandler):
    """A example of threaded TCP request handler"""
    def handle(self):
        data = self.request.recv(BUF_SIZE)
        current_thread = threading.current_thread()
        response = '%s: %s' % (current_thread.name, data)
        # print "Server sending response [current_thread name: date] = %s" % response
        self.request.send(response)

class ThreadTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    """ Nothing to add here, inherited everything necessary from parents
    """
    pass

def main():
    # Launch the server
    server = ThreadTCPServer((SERVER_HOST, SERVER_PORT), ThreadTCPRequestHandler)
    ip, port = server.server_address  # Retrieve the port number
    print ip, port, 'server'
    # Start a thread with the server -- one thread per request
    server_thread = threading.Thread(target=server.serve_forever)
    # Exit the server thread when the main thread exits
    server_thread.daemon = True
    server_thread.start()
    print 'Server loop running thread: %s' % server_thread.name

    # Run clients
    client(ip, port, "Hello from client 1")
    client(ip, port, "Hello from client 2")
    client(ip, port, "Hello from client 3")

    # Server cleanup
    server.shutdown()

if __name__ == '__main__':
    main()
ForkingMixin
import os
import socket
import threading
import SocketServer

SERVER_HOST = 'localhost'
SERVER_PORT = 0
BUF_SIZE = 1024
ECHO_MSC = 'Hello echo server!'

class ForkingClient():
    """TCP客戶端來檢測FrokingProcess服務端"""
    def __init__(self, ip, port):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.connect((ip, port))

    def run(self):
        current_process_id = os.getpid()   #獲取程序id
        print 'PID %s sending echo message to the server: %s' % (current_process_id,)
        send_data_length = self.sock.send(ECHO_MSC)
        print "Sent: %d characters, so far..." % send_data_length

        # display server response
        response = self.sock.recv(BUF_SIZE)
        print "PID %s received: %s" % (current_process_id, response[5:])

    def shutdown(self):
        """Cleanup the client socket"""
        self.sock.close()

class ForkingServerRequestHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        # Send the echo back to the client
        data = self.request.recv(BUF_SIZE)
        current_process_id = os.getpid()
        response = '%s:%s' %(current_process_id, data)
        print "Server sending response [current_process_id:data] = %s" % response
        self.request.send(response)
        return

class ForkingServer(SocketServer.ForkingMixIn, SocketServer.TCPServer):
    """Nothing to add here, inherited everything necessary from parents"""
    pass

def main():
    server = ForkingServer(SERVER_HOST, SERVER_PORT), ForkingServerRequestHandler
    ip, port = server.server_address
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.setDaemon(True)
    server_thread.start()
    print 'Server loop runing PID: %s' % os.getpid()

    # Launch the client(s)
    client1 = ForkingClient(ip, port)
    client1.run()

    client2 = ForkingClient(ip, port)
    client2.run()

    server.shutdown()
    client1.shutdown()
    client2.shutdown()
    server.socket.close()

if __name__ == '__main__':
    main()  

UDP協議

udp

UDP server

import socket

address = ('127.0.0.1', 31500)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(address)

while True:
    data, addr = s.recvfrom(2048)
    if not data:
        print "client has exist"
        break
    print "received:", data, "from", addr
    if data:
        if data == 'exit':
            print 'exiting server'
            break

s.close()

UDP Client

import socket

address = ('127.0.0.1', 31500)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

while True:
    msg = raw_input()
    if not msg:
        break
    s.sendto(msg, address)
    if msg == 'exit':
        print 'client exit'
        break

s.close()

Http協議

http

http詳解

httplib模組
  • http.HTTPConnection
    • 建立一個http型別的請求連結
  • request方法
    • conn.request(method, url[, body [, headers]])
    • 傳送一個請求
  • getresponse方法
    • 獲得一個http響應物件
  • close方法
  • read方法
httplib例項
    import httplib
    import urlparse
    import re
    import urllib

    DEFAULT_URL = 'http://www.qq.com'
    HTTP_GOOD_CODES = [httplib.OK, httplib.FOUND, httplib.MOVED_PERMANENTLY]

    def get_server_status_code(url):
        """Download just the header of a URL and return the server's status code"""
        print urlparse.urlparse(url)
        host, path = urlparse.urlparse(url)[1:3]
        print host, path
        try:
            conn = httplib.HTTPConnection(host)
            conn.request('GET', path)
            return conn.getresponse().status
        except StandardError, err:
            print err
            return None

    print get_server_status_code(DEFAULT_URL)
網頁下載資料
import httplib

REMOTE_SERVER_HOST = 'www.baidu.com'
REMOTE_SERVER_PATH = '/'

class HTTPClient:
    def __init__(self, host):
        self.host = host

def fetch(self, path):
    http = httplib.HTTP(self.host)
    http.putrequest("GET", path)
    http.puthreader("User-Agent", 'AGENT')
    http.puthreader("Host", self.host)
    http.puthereader("Accept", "*/*")
    http.endheaders()

    try:
        errcode, errmsg, headers = http.getreply()
    except Exception, e:
        print "Client failed error code: %s message: %s headers: %s" % (errcode, errmsg, headers)
    else:
        print "Got homepage from %s" % self.host

    file = http.getreply()
    return file.read()

client = HTTPClient(REMOTE_SERVER_HOST)
print client.fetch(REMOTE_SERVER_PATH)
urllib2

urllib2