什麼是C/S架構?python
C/S架構客戶端、服務端架構,C/S端軟件主要有網絡遊戲,QQ等
互聯網協議是什麼?分別介紹五層協議中每一層的功能?算法
互聯網協議:計算機之間的通訊標準
物理層:主要是基於電器特性發送高低電壓(電信號),高電壓對應數字1,低電壓對應數字0。原始比特流在物理介質上的傳輸。
數據鏈路層:定義電信號的分組方式,兩臺主機經過MAC地址進行通訊。
網絡層:引入IP地址區分不一樣的廣播/子網,選擇正確的路由找到目標主機。
傳輸層:創建端口到端口的通訊,TCP,UDP。
應用層:供操做系統或應用進行網絡通訊的標準接口(FTP,HTTP,TELNET)
基於tcp協議通訊,爲什麼創建連接須要三次握手,而斷開連接卻須要四次揮手shell
由於當Server端收到Client端的SYN鏈接請求報文後,能夠直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。可是關閉鏈接時,當Server端收到FIN報文時,極可能並不會當即關閉SOCKET,因此只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端全部的報文都發送完了,我才能發送FIN報文,所以不能一塊兒發送。故須要四次揮手.
爲什麼基於tcp協議的通訊比基於udp協議的通訊更可靠?編程
TCP協議的通訊是面向鏈接的協議,只要不獲得確認,就從新發送數據報,直到獲得對方的確認爲止。
UDP協議的通訊它是面向非鏈接的協議,它不與對方創建鏈接,而是直接就把數據包發送過去。
流式協議指的是什麼協議,數據報協議指的是什麼協議?json
流式協議指TCP
數據報協議指UDP
什麼是socket?簡述基於tcp協議的套接字通訊流程windows
socket是應用層與TCP/IP協議通訊的中間軟件抽象層,它是一組接口。它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。
因此,咱們無需深刻理解tcp/udp協議,socket已經爲咱們封裝好了,咱們只須要遵循socket的規定去編程,寫出的程序天然就是遵循tcp/udp標準的。
什麼是粘包? socket 中形成粘包的緣由是什麼? 哪些狀況會發生粘包現象?服務器
TCP協議是面向流的協議,客戶端把多個數據包合併在一塊兒發送到服務端,就產生了粘包。
因爲接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所形成的。
只有TCP有粘包現象,UDP永遠不會粘包
客戶端粘包:
發送端須要等緩衝區滿才發送出去,形成粘包(發送數據時間間隔很短,數據量很小,TCP優化算法會當作一個包發出去,產生粘包)
服務端粘包:
接收方不及時接收緩衝區的包,形成多個包接收(客戶端發送了一段數據,服務端只收了一小部分,服務端下次再收的時候仍是從緩衝區拿上次遺留的數據,產生粘包)
server.py import socket IP_PORT = ('127.0.0.1', 9999) sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_server.bind(IP_PORT) sock_server.listen(5) print("start ……") while True: conn, addr = sock_server.accept() # 阻塞直到有鏈接爲止 print('connect by ', addr) while True: try: data = conn.recv(1024) # 收到數據 if not data: break print('server 收到的數據 ', data.decode()) response = input(" input server msg >>> ").strip() conn.send(response.encode()) print("send to data:", response) except ConnectionResetError: # 適用於windows操做系統,防止客戶端斷開鏈接後死循環 break conn.close() sock_server.close() ----------------------------------------------------------------------- client.py import socket client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) IP_PORT = ('127.0.0.1', 9999) client_sock.connect(IP_PORT) while True: msg = input(" input client msg >>> ").strip() if not msg: continue client_sock.send(msg.encode()) # 發送用戶輸入的數據,必須是bytes模式 recv_data = client_sock.recv(1024) print('client recvive data ', recv_data.decode()) # 收到服務器的響應後
9. 基於tcp socket,開發簡單的遠程命令執行程序,容許用戶執行命令,並返回結果網絡
server: import socket import subprocess import struct import json def cmd_exec(cmd): """ 執行shell命令 :param cmd: :return: """ p = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode != 0: return stderr return stdout IP_PORT = ('127.0.0.1', 9999) sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_server.bind(IP_PORT) sock_server.listen(5) print("start ……") while True: conn, addr = sock_server.accept() # 阻塞直到有鏈接爲止 print('connect by ', addr) while True: try: data = conn.recv(1024) # 收到數據 if not data: break print('客戶端的命令', data.decode('GBK')) res = cmd_exec(data.decode('GBK')) # 執行cmd命令 # 構造消息頭,命令+結果長度,防止粘包 header = { 'cmd': data.decode('GBK'), 'res_size': len(res) } header_json = json.dumps(header) header_bytes = header_json.encode('utf-8') header = struct.pack('i', len(header_bytes)) # 第二步:把報頭長度發送給客戶端 conn.send(header) # 第三步:把報頭內容發送給客戶端 conn.send(header_bytes) # 第四步:再發送真實的數據 conn.sendall(res) except ConnectionResetError: # 適用於windows操做系統,防止客戶端斷開鏈接後死循環 break conn.close() sock_server.close() -------------------------------------- client: import socket import struct import json client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) IP_PORT = ('127.0.0.1', 9999) client_sock.connect(IP_PORT) while True: msg = input(" input client msg >>> ").strip() if not msg: continue client_sock.send(msg.encode('GBK')) # 發送數據 # 第一步:先收報頭 header = client_sock.recv(4) # 第二步:從報頭中解析(header數據的長度) header_size = struct.unpack('i', header)[0] print('收到報頭長度=', header_size) # 第三步:收到報頭解析出對真實數據的描述信息 header_json = client_sock.recv(header_size) header_dict = json.loads(header_json) print('收到報頭內容=', header_dict) total_size = header_dict['res_size'] # 第三步:接收真實的數據 recv_size = 0 recv_data = b'' while recv_size < total_size: data = client_sock.recv(1024) recv_data += data recv_size += len(data) print('接收數據 =', recv_data.decode('gbk', 'ignore')) # 若是設置爲ignore,則會忽略非法字符; client_sock.close()
架構
11. 基於udp協議編寫程序,實現功能socket
執行指定的命令,讓客戶端能夠查看服務端的時間
執行指定的命令,讓客戶端能夠與服務的的時間同步
server: from socket import * import time import struct import json server = socket(AF_INET, SOCK_DGRAM) server.bind(('127.0.0.1', 8880)) while True: print("waiting for message ……") # 收到報頭長度 action_type, client = server.recvfrom(1024) print('recv {}'.format(action_type.decode())) if not action_type: print('connect is lost ……') break if action_type: print('start sent to server time ……') server_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()) time.sleep(1) server.sendto(server_time.encode('utf-8'), client) server.close() -------------------------------------------------------- client: from socket import * import time import json import struct def send_msg(client, action_type, **kwargs): """ 打包消息,發送到服務器 :param action_type: :param kwargs: :return: """ # 發送cmd print('發送auth報頭內容:{}'.format(action_type)) client.sendto(action_type.encode("utf-8"), ('127.0.0.1',8880)) if __name__ == '__main__': client = socket(AF_INET, SOCK_DGRAM) while True: cmd = input("input cmd >> ").strip() if not cmd: continue send_msg(client, cmd) data, server = client.recvfrom(1024) if cmd =='gettime': print("server time is", data.decode()) print("local clock is", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) elif cmd == 'synctime': print("server time is {} start sync client clock ……".format(data.decode())) print("local clock is", data.decode()) elif cmd == 'bye': break else: print('input cmd error') client.close()