一.說明
對於TCP/udp的說明已經很多了,我在這里只是簡單的說明一下
二.套接字scoket
套接字是一種具有之前所說的“通信端點”概念的計算網絡數據結構。相當
於電話插口,沒它無法通信,這個比喻非常形象。
套接字起源於20世紀70年代加州伯克利分校版本的Unix,即BSD Unix
。又稱為“伯克利套接字”或“BSD套接字”。最初套接字被設計用在同一台
主機上多個應用程序之間的通訊,這被稱為進程間通訊或IPC。
套接字分兩種:基於文件型和基於網絡的
第一個套接字家族為AF_UNIX,表示“地址家族:UNIX”。包括
Python在內的大多數流行平台上都使用術語“地址家族”及其縮寫AF。由於兩
個進程都運行在同一台機器上,而且這些套接字是基於文件的,所以它們的底
層結構是由文件系統來支持的。可以理解為同一台電腦上,文件系統確實是不
同的進程都能進行訪問的。
第二個套接字家族為AF_INET,表示”地址家族:Internet“。還有
一種地址家族AF_INET6被用於網際協議IPv6尋址。Python 2.5中加入了一種
Linux套接字的支持:AF_NETLINK(無連接)套接字家族,讓用戶代碼與內核
代碼之間的IPC可以使用標准BSD套接字接口,這種方法更為精巧和安全。
Python只支持AF_UNIX、AF_NETLINK和AF_INET家族。網絡編程關注
AF_INET。
如果把套接字比作電話的查看——即通信的最底層結構,那主機與端
口就相當於區號和電話號碼的一對組合。一個因特網地址由網絡通信必須的主
機與端口組成。
而且另一端一定要有人接聽才行,否則會提示”對不起,您撥打的電
話是空號,請查詢后再撥“。同樣你也可能會遇到如”不能連接該服務器、服
務器無法響應“等。合法的端口范圍是0~65535,其中小於1024端口號為系統
保留端口。
三.面向連接與無連接
面向連接:通信之前一定要建立一條連接,這種通信方式也被成為”虛電路
“或”流套接字“。面向連接的通信方式提供了順序的、可靠地、不會重復的
數據傳輸,而且也不會被加上數據邊界。這意味着,每發送一份信息,可能會
被拆分成多份,每份都會不多不少地正確到達目的地,然后重新按順序拼裝起
來,傳給正等待的應用程序。
實現這種連接的主要協議就是傳輸控制協議TCP。要創建TCP套接字就
得創建時指定套接字類型為SOCK_STREAM。TCP套接字這個類型表示它作為流套
接字的特點。由於這些套接字使用網際協議IP來查找網絡中的主機,所以這樣
形成的整個系統,一般會由這兩個協議(TCP和IP)組合描述,即TCP/IP。
無連接:無需建立連接就可以通訊。但此時,數據到達的順序、可靠
性及不重復性就無法保障了。數據報會保留數據邊界,這就表示數據是整個發
送的,不會像面向連接的協議先拆分成小塊。它就相當於郵政服務一樣,郵件
和包裹不一定按照發送順序達到,有的甚至可能根本到達不到。而且網絡中的
報文可能會重復發送。
那么這么多缺點,為什么還要使用它呢?由於面向連接套接字要提供
一些保證,需要維護虛電路連接,這都是嚴重的額外負擔。數據報沒有這些負
擔,所有它會更”便宜“,通常能提供更好的性能,更適合某些場合,如現場
直播要求的實時數據講究快等。
實現這種連接的主要協議是用戶數據報協議UDP。要創建UDP套接字就
得創建時指定套接字類型為SOCK_DGRAM。這個名字源於datagram(數據報),
這些套接字使用網際協議來查找網絡主機,整個系統叫UDP/IP。
四.socket()模塊函數
使用socket模塊的socket()函數來創建套接字。語法如下:
socket(socket_family, socket_type, protocol=0)
其中socket_family不是AF_VNIX就是AF_INET,socket_type可以是
SOCK_STREAM或者SOCK_DGRAM,protocol一般不填,默認值是0。
創建一個TCP/IP套接字的語法如下:
tcpSock = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
同樣創建一個UDP/IP套接字的語法如下:
udpSock = socket.socket(socket.AF_INET,
socket.SOCK_DGRAM)
由於socket模塊中有太多屬性,所以使用"from socket import *"語
句,把socket模塊里面的所有屬性都帶到命名空間中,大幅縮短代碼。調用如
下:
tcpSock = socket(AF_INET, SOCK_STREAM)
五.套接字對象
下面是最常用的套接字對象方法:
服務器端套接字函數
socket類型 |
描述 |
s.bind() |
綁定地址(主機號 端口號對)到套接字 |
s.listen() |
開始TCP監聽 |
s.accept() |
被動接受TCP客戶端連接,(阻塞式)等待連續的到來 |
客戶端套接字函數
socket類型 |
描述 |
s.connect() |
主動初始化TCP服務器連接 |
s.connect_ex() |
connect()函數擴展版本,出錯時返回出錯碼而不是跑出異常 |
公共用途的套接字函數
socket類型 |
描述 |
s.recv() |
接受TCP數據 |
s.send() |
發送TCP數據 |
s.sendall() |
完整發送TCP數據 |
s.recvfrom() |
接受UDP數據 |
s.sendto() |
發送UDP數據 |
s.getpeername() |
連接到當前套接字的遠端地址(TCP連接) |
s.getsockname() |
獲取當前套接字的地址 |
s.getsockopt() |
返回指定套接字的參數 |
s.setsockopt() |
設置指定套接字的參數 |
s.close() |
關閉套接字 |
面向模塊的套接字函數
socket類型 |
描述 |
s.setblocking() |
設置套接字的阻塞與非阻塞模式 |
s.settimeout() |
設置阻塞套接字操作的超時時間 |
s.gettimeout() |
得到阻塞套接字操作的超時時間 |
面向文件的套接字函數
socket類型 |
描述 |
s.fileno() |
套接字的文件描述符 |
s.makefile() |
創建一個與套接字關聯的文件對象 |
六.代碼實現
TCP服務端代碼
#! /usr/bin/env python #coding=utf-8 import socket bind_ip = "" bind_port = 9999 server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind((bind_ip,bind_port)) server.listen(5) try: while True: client,add = server.accept() print "[*]你監聽的是:%s:%d" % (add[0],add[1]) while True: data = client.recv(1024) if not data: break print data data = raw_input('> ') client.send(data) # print data else: client.close() except Exception as e: print e server.close()
TCP客戶端代碼
#! /usr/bin/env python #coding=utf-8 import socket target_host = "127.0.0.1" target_port = 9999 #建立一個socket對象 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #鏈接客戶端 client.connect((target_host,target_port)) while True: data = raw_input('> ') client.send(data) #發送一些數據 #client.send("GET /HTTP/1.1\r\nHost:baidu.com\r\n\r\n") data = client.recv(4096) if not data: break print data
UDP服務端
# -*- coding: utf-8 -*- from socket import * from time import ctime HOST = '' #主機名 PORT = 21567 #端口號 BUFSIZE = 1024 #緩沖區大小1K ADDR = (HOST,PORT) udpSerSock = socket(AF_INET, SOCK_DGRAM) udpSerSock.bind(ADDR) #綁定地址到套接字 while True: #無限循環等待連接到來 try: print 'Waiting for message ....' data, addr = udpSerSock.recvfrom(BUFSIZE) #接受UDP print 'Get client msg is: ', data udpSerSock.sendto('[%s] %s' %(ctime(),data), addr) #發送UDP print 'Received from and returned to: ',addr except Exception,e: print 'Error: ',e udpSerSock.close() #關閉服務器
UDP服務端
# -*- coding: utf-8 -*- from socket import * HOST = 'localhost' #主機名 PORT = 21567 #端口號 與服務器一致 BUFSIZE = 1024 #緩沖區大小1K ADDR = (HOST,PORT) udpCliSock = socket(AF_INET, SOCK_DGRAM) while True: #無限循環等待連接到來 try: data = raw_input('>') if not data: break udpCliSock.sendto(data, ADDR) #發送數據 data,ADDR = udpCliSock.recvfrom(BUFSIZE) #接受數據 if not data: break print 'Server : ', data except Exception,e: print 'Error: ',e udpCliSock.close() #關閉客戶端