socke编程
一 客戶端和服務端架構
1 硬件c/s架構
2軟甲c/s架構
3 socket與c/s的關系:socket是為了開發c/s的
二 osi七層
?三張圖片http://www.cnblogs.com/wanghaohao/diary/2017/08/21/7404581.html?
詳細的網路通訊原理http://www.cnblogs.com/linhaifeng/articles/5937962.html
三socket編程
socket是基于應用層和TCP/IP通訊協議的中間抽象層,是一組借口,當然也可以說成是IP+port(端口)
四套接字
1基于文件類型的套接字家族AF_UNIX
unix一切皆文件,基于文件的套接字調用就是底層的文件系統來取數據,兩個套接字進程運行在同一機器上,可以通過訪問同一文件系統間接完成通訊
2基于網絡通訊的套接字家族AF_INET
3套接字工作流程
五,socket()函數模塊的用法
import socket socket.socket(socket_family,socket_type,protocal=0) socket_family 可以是 AF_UNIX 或 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 module import *'語句。使用 'from socket import *',我們就把 socket 模塊里的所有屬性都帶到我們的命名空間里了,這樣能 大幅減短我們的代碼。 例如tcpSock = socket(AF_INET, SOCK_STREAM) View Code服務端套接字函數
s.bind() ? ? ? ? ? 綁定(主機,端口號)到套接字
s.listen() ? ? ? ? ?開始TCP監聽
s.accept() ? ? ? ?被動接受TCP客戶端的鏈接,等待連接的到來
客戶端套接字函數
s.connect() ? ? ? ?主動初始化TCP服務器鏈接
s.connect_ex() ? connect()是函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
公共用的套接字函數
s.recv() 接收TCP數據s.send() 發送TCP數據(send在待發送數據量大于己端緩存區剩余空間時,數據丟失,不會發完)
s.sendall() 發送完整的TCP數據(本質就是循環調用send,sendall在待發送數據量大于己端緩存區剩余空間時,數據不丟失,循環調用send直到發完)
s.recvfrom() 接收UDP數據
s.sendto() 發送UDP數據
s.getpeername() 連接到當前套接字的遠端的地址
s.getsockname() 當前套接字的地址
s.getsockopt() 返回指定套接字的參數
s.setsockopt() 設置指定套接字的參數
s.close() 關閉套接字 面向鎖的套接字方法
s.setblocking() 設置套接字的阻塞與非阻塞模式
s.settimeout() 設置阻塞套接字操作的超時時間
s.gettimeout() 得到阻塞套接字操作的超時時間
面向文件的套接字的函數
s.fileno() 套接字的文件描述符
s.makefile() 創建一個與該套接字相關的文件
1:用打電話的流程快速描述socket通信 2:服務端和客戶端加上基于一次鏈接的循環通信 3:客戶端發送空,卡主,證明是從哪個位置卡的 服務端: from socket import * phone=socket(AF_INET,SOCK_STREAM) phone.bind(('127.0.0.1',8081)) phone.listen(5)conn,addr=phone.accept() while True:data=conn.recv(1024)print('server===>')print(data)conn.send(data.upper()) conn.close() phone.close() 客戶端: from socket import *phone=socket(AF_INET,SOCK_STREAM) phone.connect(('127.0.0.1',8081))while True:msg=input('>>: ').strip()phone.send(msg.encode('utf-8'))print('client====>')data=phone.recv(1024)print(data)說明卡的原因:緩沖區為空recv就卡住,引出原理圖4.演示客戶端斷開鏈接,服務端的情況,提供解決方法5.演示服務端不能重復接受鏈接,而服務器都是正常運行不斷來接受客戶鏈接的6:簡單演示udp 服務端 from socket import * phone=socket(AF_INET,SOCK_DGRAM) phone.bind(('127.0.0.1',8082)) while True:msg,addr=phone.recvfrom(1024)phone.sendto(msg.upper(),addr) 客戶端 from socket import * phone=socket(AF_INET,SOCK_DGRAM) while True:msg=input('>>: ')phone.sendto(msg.encode('utf-8'),('127.0.0.1',8082))msg,addr=phone.recvfrom(1024)print(msg)udp客戶端可以并發演示 udp客戶端可以輸入為空演示,說出recvfrom與recv的區別,暫且不提tcp流和udp報的概念,留到粘包去說讀者勿看:socket實驗推演流程
六 ?基于TCP的套接字
ss = socket() #創建服務器套接字 ss.bind() #把地址綁定到套接字 ss.listen() #監聽鏈接 inf_loop: #服務器無限循環cs = ss.accept() #接受客戶端鏈接comm_loop: #通訊循環cs.recv()/cs.send() #對話(接收與發送)cs.close() #關閉客戶端套接字 ss.close() #關閉服務器套接字(可選)tcp客戶端
cs = socket() # 創建客戶套接字 cs.connect() # 嘗試連接服務器 comm_loop: # 通訊循cs.send()/cs.recv() # 對話(發送/接收) cs.close() # 關閉客戶套接字socket通信流程與打電話流程類似,我們就以打電話為例套接字通信
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',9000) #電話卡 BUFSIZE=1024 #收發消息的尺寸 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機 s.bind(ip_port) #手機插卡 s.listen(5) #手機待機 conn,addr=s.accept() #手機接電話 # print(conn) # print(addr) print('接到來自%s的電話' %addr[0])msg=conn.recv(BUFSIZE) #聽消息,聽話 print(msg,type(msg))conn.send(msg.upper()) #發消息,說話 conn.close() #掛電話 s.close() #手機關機 服務端 #_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect_ex(ip_port) #撥電話 s.send('linhaifeng nb'.encode('utf-8')) #發消息,說話(只能發送字節類型) feedback=s.recv(BUFSIZE) #收消息,聽話 print(feedback.decode('utf-8'))s.close() #掛電話 客戶端加上鏈接循環與通信循環
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',8081)#電話卡 BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #買手機 s.bind(ip_port) #手機插卡 s.listen(5) #手機待機while True: #新增接收鏈接循環,可以不停的接電話conn,addr=s.accept() #手機接電話# print(conn)# print(addr)print('接到來自%s的電話' %addr[0])while True: #新增通信循環,可以不斷的通信,收發消息msg=conn.recv(BUFSIZE) #聽消息,聽話# if len(msg) == 0:break #如果不加,那么正在鏈接的客戶端突然斷開,recv便不再阻塞,死循環發生print(msg,type(msg))conn.send(msg.upper()) #發消息,說話 conn.close() #掛電話 s.close() #手機關機 服務端改進版 服務端 #_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',8081) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.connect_ex(ip_port) #撥電話while True: #新增通信循環,客戶端可以不斷發收消息msg=input('>>: ').strip()if len(msg) == 0:continues.send(msg.encode('utf-8')) #發消息,說話(只能發送字節類型) feedback=s.recv(BUFSIZE) #收消息,聽話print(feedback.decode('utf-8'))s.close() #掛電話 客戶端改進版 客戶端重啟服務端時可能會遇到
是由于你的服務端仍然存在四次揮手的time_wait狀態在占用地址(如果不懂,請深入研究1.tcp三次握手,四次揮手 2.syn洪水攻擊 3.服務器高并發情況下會有大量的time_wait狀態的優化方法)
解決方法
#加入一條socket配置,重用ip和端口 phone=socket(AF_INET,SOCK_STREAM) phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 phone.bind(('127.0.0.1',8080)) 一 發現系統存在大量TIME_WAIT狀態的連接,通過調整linux內核參數解決, vi /etc/sysctl.conf編輯文件,加入以下內容: net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 30然后執行 /sbin/sysctl -p 讓參數生效。net.ipv4.tcp_syncookies = 1 表示開啟SYN Cookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防范少量SYN攻擊,默認為0,表示關閉;net.ipv4.tcp_tw_reuse = 1 表示開啟重用。允許將TIME-WAIT sockets重新用于新的TCP連接,默認為0,表示關閉;net.ipv4.tcp_tw_recycle = 1 表示開啟TCP連接中TIME-WAIT sockets的快速回收,默認為0,表示關閉。net.ipv4.tcp_fin_timeout 修改系統默認的 TIMEOUT 時間方法二 二七基于UDP的套接字
udp是無鏈接的,先啟動那一端都不會報錯
udp服務端
ss = socket() #創建一個服務器的套接字 ss.bind() #綁定服務器套接字 inf_loop: #服務器無限循環cs = ss.recvfrom()/ss.sendto() # 對話(接收與發送) ss.close() # 關閉服務器套接字udp客戶端
cs = socket() # 創建客戶套接字 comm_loop: # 通訊循環cs.sendto()/cs.recvfrom() # 對話(發送/接收) cs.close() # 關閉客戶套接字udp套接字簡單示例
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)udp_server_client.bind(ip_port)while True:msg,addr=udp_server_client.recvfrom(BUFSIZE)print(msg,addr)udp_server_client.sendto(msg.upper(),addr) udp服務端 #_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)while True:msg=input('>>: ').strip()if not msg:continueudp_server_client.sendto(msg.encode('utf-8'),ip_port)back_msg,addr=udp_server_client.recvfrom(BUFSIZE)print(back_msg.decode('utf-8'),addr) udp客戶端qq聊天(由于udp無連接,所以可以同時多個客戶端去跟服務端通信)、
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket ip_port=('127.0.0.1',8081) udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #買手機 udp_server_sock.bind(ip_port)while True:qq_msg,addr=udp_server_sock.recvfrom(1024)print('來自[%s:%s]的一條消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))back_msg=input('回復消息: ').strip()udp_server_sock.sendto(back_msg.encode('utf-8'),addr)udp服務端 服務端 #_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket BUFSIZE=1024 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)qq_name_dic={'狗哥alex':('127.0.0.1',8081),'瞎驢':('127.0.0.1',8081),'一棵樹':('127.0.0.1',8081),'武大郎':('127.0.0.1',8081), }while True:qq_name=input('請選擇聊天對象: ').strip()while True:msg=input('請輸入消息,回車發送: ').strip()if msg == 'quit':breakif not msg or not qq_name or qq_name not in qq_name_dic:continueudp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)print('來自[%s:%s]的一條消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))udp_client_socket.close()udp客戶端1 客戶端一 #_*_coding:utf-8_*_ __author__ = 'Linhaifeng' import socket BUFSIZE=1024 udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)qq_name_dic={'狗哥alex':('127.0.0.1',8081),'瞎驢':('127.0.0.1',8081),'一棵樹':('127.0.0.1',8081),'武大郎':('127.0.0.1',8081), }while True:qq_name=input('請選擇聊天對象: ').strip()while True:msg=input('請輸入消息,回車發送: ').strip()if msg == 'quit':breakif not msg or not qq_name or qq_name not in qq_name_dic:continueudp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)print('來自[%s:%s]的一條消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))udp_client_socket.close()udp客戶端2 kehuduan 2時間服務器
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' from socket import * from time import strftimeip_port=('127.0.0.1',9000) bufsize=1024tcp_server=socket(AF_INET,SOCK_DGRAM) tcp_server.bind(ip_port)while True:msg,addr=tcp_server.recvfrom(bufsize)print('===>',msg)if not msg:time_fmt='%Y-%m-%d %X'else:time_fmt=msg.decode('utf-8')back_msg=strftime(time_fmt)tcp_server.sendto(back_msg.encode('utf-8'),addr)tcp_server.close()ntp服務端?
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' from socket import * ip_port=('127.0.0.1',9000) bufsize=1024tcp_client=socket(AF_INET,SOCK_DGRAM)while True:msg=input('請輸入時間格式(例%Y %m %d)>>: ').strip()tcp_client.sendto(msg.encode('utf-8'),ip_port)data=tcp_client.recv(bufsize)print(data.decode('utf-8'))tcp_client.close()?
轉載于:https://www.cnblogs.com/wanghaohao/p/7404547.html
總結
- 上一篇: WebApi 的CRUD 的方法的应用
- 下一篇: postfix邮件服务