1. 程式人生 > >Python網絡編程篇之socket

Python網絡編程篇之socket

選擇 linu 隊列 ufs 通信機制 ber 最大連接數 新的 例如

1 socket

插座?呵呵,想多了,翻譯過來意思是套接字!

A network socket is an internal endpoint for sending or receiving data at a single node in a computer network. Concretely, it is a representation of this endpoint in networking software (protocol stack), such as an entry in a table (listing communication protocol, destination, status, etc.), and is a form of system resource.

socket是一種進程間通信機制(inter process communication ,IPC),提供一種供應用程序訪問通信協議的操作系統調用,並且通過將socket與Unix系統文件描述符相整合,使得網絡讀寫數據(或者服務調用)和讀寫本地文件一樣容易。很顯然,這貨已經離插座越來越遠了,已經完全不再是硬件上的物件,而是一序列的“指令” ,按漢語的理解,已經具備了“套接”(建立網絡通訊或進程間通訊)和“字”(可交互的有序指令串)的概念。 2 socket address :主機-端口對 如果一個套接字像一個電話插孔——允許通信的一些基礎設施,name主機名和端口號就像區號和電話號碼的組合。 有效的端口號範圍0-65535,盡管小於1024的端口號預留給了系統,Linux和mac os 可以在/etc/services文件中找到預留端口號的列表。 A socket address is the combination of an IP address and a port number, much like one end of a telephone connection is the combination of a phone number and a particular extension. Sockets need not have an address (for example for only sending data), but if a program binds a socket to an address, the socket can be used to receive data sent to that address. Based on this address, internet sockets deliver incoming data packets to the appropriate application process or thread. 有兩種類型的套接字:基於文件的和面向網絡的 AF:address family地址家族 AF_UNIX 面向文件 AF_INET面向網絡

參數一:地址簇

  socket.AF_INET IPv4(默認)
  socket.AF_INET6 IPv6

  socket.AF_UNIX 只能夠用於單一的Unix系統進程間通信

參數二:類型

  socket.SOCK_STREAM  流式socket , for TCP (默認)
  socket.SOCK_DGRAM   數據報式socket , for UDP

  socket.SOCK_RAW 原始套接字,普通的套接字無法處理ICMP、IGMP等網絡報文,而SOCK_RAW可以;其次,SOCK_RAW也可以處理特殊的IPv4報文;此外,利用原始套接字,可以通過IP_HDRINCL套接字選項由用戶構造IP頭。
  socket.SOCK_RDM 是一種可靠的UDP形式,即保證交付數據報但不保證順序。SOCK_RAM用來提供對原始協議的低級訪問,在需要執行某些特殊操作時使用,如發送ICMP報文。SOCK_RAM通常僅限於高級用戶或管理員運行的程序使用。
  socket.SOCK_SEQPACKET 可靠的連續數據包服務

參數三:協議

  0  (默認)與特定的地址家族相關的協議,如果是 0 ,則系統就會根據地址格式和套接類別,自動選擇一個合適的協議

3.面向連接的套接字也稱為虛擬電路或流套接字

面向連接的通信提供序列化的、可靠的和不重復的數據交付,並且沒有記錄邊界,實現這種連接的主要協議是TCP(傳輸控制協議)。

創建TCP套接字必須使用SOCK_STREAM作為套接字類型。

無連接的傳輸無法保證傳輸的內容的順序性、可靠性。無連接傳輸的優勢是沒有維護虛擬電路連接帶來的開銷,從而擁有更低的成本。實現無連接的主要協議是UDP(用戶數據報協議),創建UDP套接字必須使用SOCK_DGRAM作為套接字類型。

4.Socket 參數介紹

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()
  套接字的文件描述符

5.創建socket

socket.socket(AddressFamily, Type)

函數 socket.socket 創建?個 socket, 返回該 socket 的描述符, 該函數帶有兩個參數:

Address Family: 可以選擇 AF_INET( ?於 Internet 進程間通信) 或者AF_UNIX( ?於同?臺機器進程間通信) ,實際?作中常?AF_INET

Type: 套接字類型, 可以是 SOCK_STREAM( 流式套接字, 主要?於TCP 協議) 或者 SOCK_DGRAM( 數據報套接字, 主要?於 UDP 協議)創建?個tcp socket( tcp套接字)

5.1創建?個tcp sockettcp套接字)

TCP通信需要建立一個可靠連接的過程,而且通信雙方以流的形式發送數據。

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print Socket Created

tcp_server

# -*- coding: utf-8 -*-
# 2017/11/25 16:31
import socket
import threading
import time
def dealClient(sock, addr):
    #第四步:接收傳來的數據,並發送給對方數據
    print(Accept new connection from %s:%s... % addr)
    sock.send(bHello,I am server!)
    while True:
        data = sock.recv(1024)
        time.sleep(1)
        if not data or data.decode(utf-8) == exit:
            break
        print(-->>%s! % data.decode(utf-8))
        sock.send((Loop_Msg: %s! % data.decode(utf-8)).encode(utf-8))
    #第五步:關閉套接字
    sock.close()
    print(Connection from %s:%s closed. % addr)

if __name__=="__main__":
    #第一步:創建一個基於IPv4和TCP協議的Socket
    # 套接字綁定的IP(127.0.0.1為本機ip)與端口
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((127.0.0.1, 9999))
    #第二步:監聽連接
    s.listen(5)
    print(Waiting for connection...)
    while True:
        # 第三步:接受一個新連接:
        sock, addr = s.accept()
        # 創建新線程來處理TCP連接:
        t = threading.Thread(target=dealClient, args=(sock, addr))
        t.start()

tcp_client

# -*- coding: utf-8 -*-
# 2017/11/25 16:32
import socket
#初始化Socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#連接目標的ip和端口
s.connect((127.0.0.1, 9999))
# 接收消息
print(-->>+s.recv(1024).decode(utf-8))
# 發送消息
s.send(bHello,I am a client)
print(-->>+s.recv(1024).decode(utf-8))
s.send(bexit)
#關閉套接字
s.close()

5.2創建?個udp socketudp套接字)

使用UDP協議時,不需要建立連接,只需要知道對方的ip和port,就可以直接發數據包,但是不關心是否能到達目的端。

UDP --- 用戶數據報協議, 是一個無連接的簡單的面向數據報的運輸層協議。
UDP不提供可靠性, 它只是把應用程序傳給IP層的數據報發送出去,但是並不能保證它們能到達目的地。
由於UDP在傳輸數據報前不用在客戶和服務器之間建立一個連接,且沒有超時重發等機制,傳輸速度很快。
UDP是一種面向無連接的協議, 每個數據報都是一個獨立的信息,包括完整的源地址或目的的地址,
它在網絡上以任何可能的路徑傳往目的地, 因此能否到達?的地, 到達目的地的時間以及內容的正確性都是不能被保證的。

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
print Socket Created

udp_server

# -*- coding: utf-8 -*-
# 2017/11/25 16:38
import socket
#創建Socket,綁定指定的ip和端口
#SOCK_DGRAM指定了這個Socket的類型是UDP。綁定端口和TCP一樣。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((127.0.0.1, 9999))
print(Bind UDP on 9999...)
while True:
    # 直接發送數據和接收數據
    data, addr = s.recvfrom(1024)
    print(Received from %s:%s. %(addr,data))
    s.sendto(bHello,addr)

udp_client

# -*- coding: utf-8 -*-
# 2017/11/25 16:39
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [bHello, bWorld]:
    # 發送數據:
    s.sendto(data, (127.0.0.1, 9999))
    # 接收數據:
    print(s.recv(1024).decode(utf-8))
s.close()

一下均基於tcp開發:

一對一

server

#__author: greg
#date: 2017/9/16 16:11
import socket
ip_port = (127.0.0.1,8888)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)#最大排隊數,能開多少人
print ("服務端啟動...")
# conn,addr= sk.accept()
# while True:
#     client_data=conn.recv(1024)
#     if str(client_data,"utf8")==‘exit‘:
#         break
#     print (str(client_data,"utf8"))
#     server_response=input(">>>")
#     conn.sendall(bytes(server_response,"utf8"))
# conn.close()

while True:
    conn,address = sk.accept()
    print(address)
    while True:
        try:
            client_data=conn.recv(1024)
        except:
            print("意外中斷")
            break
        print (str(client_data,"utf8"))
        server_response=input(">>>")
        conn.sendall(bytes(server_response,"utf8"))
    conn.close()

client

#__author: greg
#date: 2017/9/16 16:11
import socket
ip_port = (127.0.0.1,8888)
sk = socket.socket()
sk.connect(ip_port)
print ("客戶端啟動:")
# while True:
#     inp = input(‘>>>‘)
#     sk.sendall(bytes(inp,"utf8"))
#     if inp == ‘exit‘:
#         break
#     server_response=sk.recv(1024)
#     print (str(server_response,"utf8"))
# sk.close()

while True:
    inp=input(>>>)
    if inp=="exit":
        break
    sk.send(bytes(inp,utf8))
    data=sk.recv(1024)
    print(str(data,"utf8"))
sk.close()

一對多,簡單並發

server

#__author: greg
#date: 2017/9/16 16:27
import socketserver
class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        print ("服務端啟動...")
        while True:
            conn = self.request
            print (self.client_address)
            while True:
                client_data=conn.recv(1024)
                print (str(client_data,"utf8"))
                print ("waiting...")
                conn.sendall(client_data)
            conn.close()
            
if __name__ == __main__:
    server = socketserver.ThreadingTCPServer((127.0.0.1,8091),MyServer)
    server.serve_forever()

client

#__author: greg
#date: 2017/9/16 16:27

import socket

ip_port = (127.0.0.1,8091)
sk = socket.socket()
sk.connect(ip_port)
print ("客戶端啟動:")
while True:
    inp = input(>>>)
    sk.sendall(bytes(inp,"utf8"))
    if inp == exit:
        break
    server_response=sk.recv(1024)
    print (str(server_response,"utf8"))
sk.close()

聊天並發實例

server

#__author: greg
#date: 2017/9/16 20:55
import socketserver
class MyServer(socketserver.BaseRequestHandler):
    def handle(self): #handle父類有handle方法
        print ("服務端啟動...")
        while True:
            conn = self.request
            print (self.client_address)
            while True:
                client_data=conn.recv(1024)
                print (str(client_data,"utf8"))
                print ("waiting...")
                server_response=input(">>>")
                conn.sendall(bytes(server_response,"utf8"))
                # conn.sendall(client_data)
            conn.close()
            # print self.request,self.client_address,self.server
            
if __name__ == __main__:
    server = socketserver.ThreadingTCPServer((127.0.0.1,8098),MyServer)
    server.serve_forever()

client

#__author: greg
#date: 2017/9/16 20:54
import socket
ip_port = (127.0.0.1,8098)
sk = socket.socket()
sk.connect(ip_port)
print ("客戶端啟動:")
while True:
    inp = input(>>>)
    sk.sendall(bytes(inp,"utf8"))
    server_response=sk.recv(1024)
    print (str(server_response,"utf8"))
    if inp == exit:
        break
sk.close()

下一篇:socketserver

Python網絡編程篇之socket