OpenHarmony與蘋果的夢幻聯動——服務器端(上)
一、前言
承接上篇文章的??項目介紹??,本篇文章將詳細介紹如何在小熊派搭建TCP服務器,處理來自各種客戶端的請求。本篇文章適合于小型系統和標準系統,大家可以將本項目移植到符合以上條件的開發板上。配置好對應的編譯規則即可。
二、準備工作
1、小熊派-鴻蒙·叔(BearPi-HM Micro)一臺。
2、已經配置好小熊派開發環境的電腦一臺。
3、將小熊派接入到局域網。
三、流程簡介
編寫TCP服務器的C文件 --> 配置BUILD.gn --> 將我們的代碼添加編譯–>編譯燒錄。
四、網絡開發基礎知識
在OpenHarmony上進行網絡開發與在Linux上進行網絡開發十分相似,采用的是C語言的套接字(Socket)開發。
進行網絡開發之前,如果沒有相關基礎,建議先了解一下OSI網絡模型與TCP/IP協議的關系。本次用到的是用Socket進行傳輸層的開發,使用了到了一點點應用層HTTP的協議。
(1) 什么是Socket
在計算機通信領域,socket 被翻譯為“套接字”,它是計算機之間進行通信的一種約定或一種方式。通過 socket 這種約定,一臺計算機可以接收其他計算機的數據,也可以向其他計算機發送數據。
總結為一句就是:socket就是整合好TCP/IP協議的一個工具。讓我們無需過度關注于底層協議的實現,直接用封裝好的socket就行了。
(2) TCP通信流程
在開始使用socket之前,我們要知道TCP服務器端與TCP客戶端進行通信的流程。
我們小熊派要實現的是圖中的9右半部分,即TCP服務器端。我們一步一步的看,每一步都有對應的代碼來實現。
第一步:socket()函數用來創建socket套接字,可以理解為面對對象中的創建對象,但是不等同。
第二步:bind()函數用來綁定IP和端口,即選擇你的TCP服務器在哪個IP和端口提供服務。
第三步:listen()函數用來監聽上一步中選擇的IP和端口。
第四步:accept()函數用來等待來自客戶端的連接,即進入阻塞狀態。
第五步:read()函數會在有客戶請求時,讀取客戶端發送的請求數據。
第六步:write()函數可以給客戶端返回數據,該操作可選,也可以不給客戶端返回任何數據。
第七步:read()和write()操作可以一直反復執行,即互相不斷通信,當通信完成時,執行close函數關閉套接字。
(3) 相關函數講解
創建socket套接字
int socket(int domain,int type,int protocol);
參數介紹
- domain:協議域,又稱協議族(family),常用的協議族有 AFL INET、AF INET6、AF LOCAL(或稱AF UNIX, Unix成socket) AF ROUTE 等。協議族決定了 socket 的地址類型,在通信中必須采用對應的地址,如AF INET 決定了要用 ipv4 地址 。 (32位的》與端口號(16位的)的組合,AF UNIX 決定了要用一個絕對路徑名作為地址。
- type:指定 Socket 類型。常用的 socket 類型有 SOCK STREAM、SOCK DGRAM、SOCK RAW 、SOCK PACKET、 SOCK SEQPACKET 等。流式 socket (SOCK STREAM)是一種面向連接的 Socket, 針對于面向連接的 TCP 。 服務應用。數據報式 socket(SOCK DGRAM) 是一種無連接的 Socket,對應于 無連接的 UDP 服務應用。
- protocol: 指定協議。常用協議有 IPPROTO TCP、IPPROTO UDP、IPPROTO STCP、IPPROTO TIPC 等,分別對應TCP 傳輸協議,UDP 傳輸協議、STCP 傳輸協議、TIPC 傳輸協議。參數為o時,會自動選擇第二個參數類型對應的默認協議。
注意:type 和protocol 不可以隨意組合,如SOCK STREAM 不可以跟 1PPROTOUDP 組合。
返回值: 如果調用成功就返口新創建的套接字的描述符,如果大敗就返回INVALID SOCKET(Linux 下失敗返回-1)。
綁定端口函數
int bind(int socketfd,const struct sockaddr *addr,socklen_t addrlen)
參數介紹
- socketfd:—個標識己連接套接口的描述字。
- address:是個sockaddr結構指針,該結構中包含了要結合的地址和端口號。
- address_len:確定 address 緩沖區的長度。
注意:其中 sockaddr這個地址結構根據地址創建 socket 時的地址協議族的不同而不同。
返回值:如果函數執行成功,返回值為0,否則為SOCKET_ERROR。
開始監聽函數
int listen(int socketfd,int backlog)
參數介紹
- socketfd:要監聽的socket描述字。
- backlog:相應socket可以排隊的最大連接個數。
等待連接阻塞函數
int accept(int socketfd,struct sockaddr *addr, socklen_t *addrlen);
參數介紹
- socketfd:就是上面解釋中的監聽套接字,這個套接字用來監聽一個端口,當有一個客戶與服務器連接時,它使用這個個端口號,而此時這個端口號正與這個套接字關聯。當然客戶不知道套接字這些細節,已只知道一個地址和一個端口號。
- sockaddr:結果參數,它用來接受一個返回值,這返回值指定客戶端的地址,當然這個地址是通過某個地址結構來描述的,用戶應該知道這一個什么樣的地址結構。如果對客戶的地址不感興趣,那么可以把這個值設置為NULL。
- len:它也是結果的參數,用來接受上述 addr 的結構的大小的,已指明 addr 結構所占有的宇節個數。同樣的,它也可以被設置為NULL。
注意:accept默認會阻塞進程,直到有一個客戶連接建立后返回,它返回的是一個新可用的套接字,這個套接字是連接套接字。
返回值:成功返回客戶端的文件貓述符,失敗返回-1。一如果accept成功返回,則服務器與容戶
己經正確建立連接了,此時服務器通過accept返口的套接字來完成與客戶的通信。
五、連接函數
int connnect(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
參數介紹
- socketfd:客戶端socket的描述字。
- socketaddr:服務器的socket地址。
- addrlen:socket地址的長度
發送函數
發送函數有兩個:
ssize_t write(int sockfd,const void *buf,szie_t nbytes);
參數介紹
- sockfd為要寫入文件的描述符。
- buf為要寫入數據的緩沖區地址。
- nbytes為要寫入的數據的字節書。
返回值:成功返回寫入的字節數,失敗返回-1。
int send(int sockfd,const void *buf,int len,int flags)
參數介紹
- sockfd為要寫入文件的描述符。
- buf為要寫入數據的緩沖區地址。
- len為要寫入的數據的字節書。
- flags有以下選擇,MSG_ DONTROUTE 為不查找路由表;MSG_OOB為接受或發送帶外數據 ;MSG PEEK為查看數據,且不從系統緩沖區移走數據;MSG WAITALL為等待任何數據;0和write函數的操作一樣。
返回值:成功返回寫入的字節數,失敗返回-1。
接受函數
接受函數同樣有兩個:
ssize_t read(int sockfd,const void *buf,szie_t nbytes);
參數介紹
- sockfd為要讀取文件的描述符。
- buf為要讀取數據的緩沖區地址。
- nbytes為要讀取的數據的字節書。
返回值:成功返回讀取到的字節數,失敗返回-1。
int recv(int sockfd,const void *buf,int len,int flags)
參數介紹
- sockfd為要寫入文件的描述符。
- buf為要寫入數據的緩沖區地址。
- len為要寫入的數據的字節書。
- flags有以下選擇,MSG_ DONTROUTE 為不查找路由表;MSG_OOB為接受或發送帶外數據 ;MSG PEEK為查看數據,且不從系統緩沖區移走數據;MSG WAITALL為等待任何數據;0和write函數的操作一樣。
返回值:成功返回寫入的字節數,失敗返回-1。