之前在編寫四層發現的代碼時都是使用TCP/UDP協議, 但是看了很多的代碼都是用socket來完成的,所以就 來學習一下socket。
什麼是socket
網絡中的兩臺主機之間進行通信,本質上是主機中所 運行的進程之間的通信,兩個進程如果需要進行通信 ,最基本的前提是每一個進程要有一個唯一的標識。
在本地進程通信中可以使用PID來唯一標識一個程, 但PID在 本地是唯一,可以用 "IP地+ 協議+端口號" 來組成唯一標識的網絡進程,這就是socket。
無論使用何種網絡協議,最本質上都是在進行數據 的收發,發和收,這兩個動作就是socket處理數據 的主要方式。
socket的工作流程
socket 採用C/S 模式,分爲服務端和客戶端
服務端數據處理流程
創建socket -> 綁定到地址和端口 -> 等待連接 -> 開始通信-> 關閉連接
客戶端數據處理流程
創建socket -> 等待連接 -> 開始通信-> 關閉連接
客戶端沒有綁定地址和端口,是由於客戶端進程採用的是隨機端口,當客戶端要去連接目標時,會由系統自定分配一個端口號和自身ip地址去組合
socket_tcp_udp_client_service.py
#!/usr/bin/env python3
#coding=utf-8
import sys
import socket
import time
import os
import threading
ServerIp='192.168.1.1'
ServerPort=9999
def tcp_client_run():
global ServerIp
global ServerPort
print("tcp socket client connect to: ",ServerIp, ServerPort)
myServerPort=int(ServerPort)
#創建一個socket的類,socket.AF_INET指的是IPV4,SOCK_STREAM指的是TCP協議,UDP協議對應socket_DRAM
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#連接PC服務器的端口,參數是(IP/域名,端口)
ServerPort_=(ServerIp,myServerPort)
# connect_ex是connect()函數的擴展版本,出錯時返回出錯碼,而不是拋出異常
s.connect_ex(ServerPort_)
#接受服務器的消息,限制接受大小爲1kb=1024b=128KB
while True:
try:
#接受消息,並打印,解碼用utf-8
print("Local:OK?")
print(s.recv(1024).decode('utf-8'))
time.sleep(1)
#發送消息,注意用二進制的數據傳輸,因爲發送的實際是一個數據流
data=(('Client'.encode('utf-8')))
s.send(data)
except:
break
print("Connection is closed by Server")
#定義一個接受和發送數據的函數,5次發送後即在服務器端強行關閉連接。
def tcplink(sock,addr):
global ServerIp
print("Accept the new connection from %s:%s"%addr)
sock.send(b"Welcome!Here is the Sever ")
n=int(5)
while n>0:
data=sock.recv(1024)
time.sleep(1)
sock.send(('Server==>I have heard you.{:s}:{:d},{:d} talks remaining\n'.format(addr[0],addr[1],n-1)).encode('utf-8'))
sock.send(('Server==>You said:{:s}\n'.format(data.decode('utf-8'))).encode('utf-8'))
n-=1
sock.close()
print('Connection from %s is close.'%addr[0])
def tcp_service_run():
global ServerIp
global ServerPort
print("socket tcp service connect to: ",ServerIp, ServerPort)
#創建socket類的實例,並綁定和監聽,閒置最大連接數爲2.
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((ServerIp,int(ServerPort)))
s.listen(2)
print('server is listening,waiting for connection......')
#創建一個新的進程來處理客戶端的請求
while True :
sock,addr=s.accept()
#注意accept方法得到的是一個socket類和一個端口地址的元組。用print命令很容易驗證。
print(sock,addr)
try:
t=threading.Thread(target=tcplink,args=(sock,addr))
tname=t.name
print('Now {:s} is running for connection from {:s}'.format(tname,addr[0]))
t.start()
except :
print("Server is block")
def udp_client_run():
global ServerIp
global ServerPort
print("udp socket client connect to: ",ServerIp, ServerPort)
ip_port=(ServerIp,int(ServerPort))
BUFSIZE=1024
udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
msg=input('input msg>>: ').strip()
if not msg:continue
udp_server_client.sendto(msg.encode('utf-8'),ip_port)
back_msg,addr=udp_server_client.recvfrom(BUFSIZE)
print(back_msg.decode('utf-8'),addr)
print("Connection is closed by client")
def udp_service_run():
global ServerIp
global ServerPort
print("udp socket service connect to: ",ServerIp, ServerPort)
ip_port=(ServerIp,int(ServerPort))
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)
print("Connection is closed by service")
if __name__ == '__main__':
if len(sys.argv) == 3 :
ServerIp=sys.argv[1]
ServerPort=sys.argv[2]
serviceType=input('is client or service?: ').strip()
protocolType=input('is tcp or udp?: ').strip()
if protocolType=='tcp':
if serviceType=='client':
tcp_client_run()
else:
tcp_service_run()
else:
if serviceType=='client':
udp_client_run()
else:
udp_service_run()
else:
print("Use: python3 tcp_client.py 192.168.1.1 9999")
客戶端和服務端都執行 python3 tcp_client.py 192.168.1.1 9999來測試,選擇是客戶端還是服務器,tcp還是udp
之後可以正常通信。