注:本文僅爲最近工作中用到的socket的整理
最近在做音視頻項目,socket用的就比較多了,以前項目裏也有隻是僅限於websocket有個心跳包的那種長連接。不過接收的都是字符串的形式。
今天我要聊的是最近用到的短連接,就是用socket進行tcp通訊。
先說一哈基本流程:連接->發送包->接收包->關閉
就這麼快,拿完數據就把流關閉了。
一個包一個包發的是什麼? byte[] 數據!
也就是發送包和接收包是以字節流的形式來完成的。
這就涉及到一個字節轉換的問題,一個int佔用4個字節。
比如說我要發送 int 1 那麼發送的其實是一串[ 0 , 0 , 0 , 1 ]的數據包。
但是不會這麼簡單,一般都是這樣定義的:
服務器和客戶端會約定一個數據結構,一般是C語言的那種形式
發送:
struct head //包頭(這個是不和服務器約定好的,不會變的,不管接收還是發送都是這個格式)
{
int head_msg; //頭信息,和服務器的約定,比如說約定頭信息爲10086當我讀取的時候讀到是10086證明是服務器發出的包,我們才進行解析,其他的就不用管了。
int length; //包長度,一般爲總長度,就是head+body的長度,一個int是4個字節,那麼這個head的長度就是4+4+4=12個字節,這裏length就要填12。
int command_id; //命令碼,假如約定命令碼100是查詢用戶數據,這裏就填100。
}
struct body
{
int device_id; //設備的id。
}
接收:
struct head
{
int head_msg; //和上面一樣。
int length; //假如接收到的length爲80,那麼80-12=68就是body的字節長度。
int command_id; //命令碼,假如約定命令碼200是查詢用戶數據的返回,那麼解析到這裏是200就進行相關解析。
}
struct body
{
int device_id; //剛剛查詢的設備的id。
int user_id; //設備上用戶的id。
char[64] user_name; //設備上用戶名。
}
以上只是舉例,講一個約定數據結構的基本概念,實際項目根據具體需求而定。
比如說上面的發送命令,包頭是3個int 那麼就是3*4=12個字節,body是一個int那麼就是4個字節,12+4=16個字節,所以按上面的格式發送命令的話,就是16個字節了。
服務器接收的話,先是讀取12個字節,因爲是約定的包頭,不會改變的。
然後讀取包頭的裏的command_id命令碼,就知道我body只有一個int,只需要再讀取4個字節就可以了。
同理,客戶端這邊接收到服務器發送的消息也是這種讀法。
最後用代碼簡單的講一下
連接服務器
Socket socket = new Socket(HOST, PORT);//HOST和PORT分別是服務器的ip地址的端口號
是否連接成功(連接服務器後調用這個檢測是否連接成功,成功後就可以交互數據了)
socket.isConnected()
輸入流和輸出流,用於發送數據到服務器,和讀取服務器發送過來的數據。
OutputStream outputStream = socket.getOutputStream();//獲取輸出流,就是我給服務器發數據的流。
InputStream inputStream = socket.getInputStream();//獲取輸入流,就是服務器給我發送的 我需要讀取的數據
向服務器發送一個數據包
outPutStream.write(byte b[]);//byte b[]是上邊講的head+body轉成字節流後的byte數組
讀取服務器發送過來的數據包
byte[] head = new byte[12];//先讀取頭,約定的不變的長度。
inputStream.read(head);//然後根據上面講到的head數據結構讀取到包的長度。
//假如包長度是200那麼,200-12=178就是body的長度
byte[] body= new byte[178];//再繼續讀取178位
inputStream.read(body);//然後就可以解析服務器的數據了。
一個完整的發送和讀取的流基本就這樣完成了
上面說了,這個TCP是短連接,用完就關閉。
那麼,解析數據完成後需要關閉socket,關閉需要順序,否則會拋出異常。
if (socket != null) {
if (socket.isConnected()) {
if (inputStream != null) inputStream.close();
if (outputStream != null) outputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
至此,一個socket的全部操作就完成了。
這就是我最近學習到的socket的相關知識,都是從項目中學到的。有興趣的可以一起交流,若有錯漏歡迎指正。