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