【学习笔记】在windows下进行基于TCP的本地客户端和服务端socket通信
文章目錄
- socket介紹
- java中使用socket
- 基于tcp的socket通信
- 使用ServerSocket類創(chuàng)建一個web服務(wù)器:(java)
- windows下的基于tcp的socket編程(c++寫)
- InetAddress 類的方法
- 附錄1 TCP UDP
- 附錄2 websocket
socket介紹
Socket的英文原義是“孔”或“插座”。在編程中,Socket被稱做 套接字,是網(wǎng)絡(luò)通信中的一種約定。
Socket編程用于解決我們 客戶端與 服務(wù)端之間通信的問題。
套接字使用TCP提供了兩臺計算機(jī)之間的通信機(jī)制。 客戶端程序創(chuàng)建一個套接字,并嘗試連接服務(wù)器的套接字。
當(dāng)連接建立時,服務(wù)器會創(chuàng)建一個 Socket 對象。客戶端和服務(wù)器現(xiàn)在可以通過對 Socket 對象的寫入和讀取來進(jìn)行通信。
java.net.Socket 類代表一個套接字,并且java.net.ServerSocket 類為服務(wù)器程序提供了一種來監(jiān)聽客戶端,并與他們建立連接的機(jī)制。
TCP 是一個雙向的通信協(xié)議,因此數(shù)據(jù)可以通過兩個數(shù)據(jù)流在同一時間發(fā)送
java中的serversocket類:用于創(chuàng)建Socket套接字的服務(wù)端,而Socket類的作用是創(chuàng)建Socket的客戶端。代碼層面是用Socket類去連接ServerSocket類,即客戶端主動連接服務(wù)端。
每一個socket都有一個輸出流和一個輸入流,客戶端的輸出流連接到服務(wù)器端的輸入流,而客戶端的輸入流連接到服務(wù)器端的輸出流。
例:
java中使用socket
服務(wù)端:
public static void main(String[]args){try{ServerSocket socket = new ServerSocket(8088);System.out.println("阻塞開始:"+System.currentTimeMillis());socket.accept();System.out.println("server阻塞結(jié)束:"+System.currentTimeMillis());socket.close();} catch (IOException e) {e.printStackTrace();}}現(xiàn)在寫客戶端:
public static void main(String[]args){try {System.out.println("client連接準(zhǔn)備"+System.currentTimeMillis());Socket socket = new Socket("localhost",8088);System.out.println("連接結(jié)束:"+System.currentTimeMillis());socket.close();}catch (UnknownHostException e){e.printStackTrace();}catch (IOException E){E.printStackTrace();}}
(只有啟動客戶端時才輸出阻塞結(jié)束的一行,當(dāng)不啟動時保持阻塞狀態(tài))
new Socket(“www.csdn.net”,80);可以把參數(shù)寫成其他IP地址或者域名,如果寫成域名,就會使用DNS服務(wù)轉(zhuǎn)成IP地址再訪問服務(wù)端。
基于tcp的socket通信
TCP提供基于“流”的“長連接”的數(shù)據(jù)傳遞,發(fā)送的數(shù)據(jù)帶有順序性。TCP是一種流協(xié)議,以流為單位進(jìn)行數(shù)據(jù)傳輸。
什么是長連接?長連接可以實現(xiàn)當(dāng)服務(wù)端與客戶端連接成功后連續(xù)地傳輸數(shù)據(jù),在這個過程中,連接保持開啟的狀態(tài),數(shù)據(jù)傳輸完畢后連接不關(guān)閉。長連接是指建立Socket連接后,無論是否使用這個連接,該連接都保持連接的狀態(tài)。
什么是短連接?短連接是當(dāng)服務(wù)端與客戶端連接成功后開始傳輸數(shù)據(jù),數(shù)據(jù)傳輸完畢后則連接立即關(guān)閉,如果還想再次傳輸數(shù)據(jù),則需要再創(chuàng)建新的連接進(jìn)行數(shù)據(jù)傳輸。
如何建立連接呢?需要客戶端與服務(wù)端進(jìn)行三次握手,握手成功后說明客戶端與服務(wù)端能實現(xiàn)數(shù)據(jù)通信。UDP是無連接協(xié)議,也就是客戶端與服務(wù)端沒有確認(rèn)彼此都存在的握手過程,因此不存在長連接與短鏈接概念。
1)長連接的優(yōu)缺點
1)優(yōu)點:除了第一次之外,客戶端不需要每次傳輸數(shù)據(jù)時都先與服務(wù)端進(jìn)行握手,這樣就減少了握手確認(rèn)的時間,直接傳輸數(shù)據(jù),提高程序運(yùn)行效率。
2)缺點:在服務(wù)端保存多個Socket對象,大量占用服務(wù)器資源。
(2)短連接的優(yōu)缺點
1)優(yōu)點:在服務(wù)端不需要保存多個Socket對象,降低內(nèi)存占用率。
2)缺點:每次傳輸數(shù)據(jù)前都要重新創(chuàng)建連接,也就是每次都要進(jìn)行3次握手,增加處理的時間。
使用ServerSocket類創(chuàng)建一個web服務(wù)器:(java)
public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(6666);Socket socket = serverSocket.accept();InputStream inputStream = socket.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream);BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String getString = "";while (!"".equals(getString = bufferedReader.readLine())) {System.out.println(getString);}OutputStream outputStream = socket.getOutputStream();outputStream.write("HTTP/1.1 200 OK\r\n\r\n".getBytes());outputStream.write("<html><body>< a href=' '>i am baidu.com welcome you!</a></body></html>".getBytes());outputStream.flush();inputStream.close();outputStream.close();socket.close();serverSocket.close();}(這是書上的代碼,很奇怪打開瀏覽器后打不開)
windows下的基于tcp的socket編程(c++寫)
本地TCP客戶端往本地TCP服務(wù)端發(fā)送數(shù)據(jù),TCP服務(wù)端收到數(shù)據(jù)則會打印輸出,同時把原數(shù)據(jù)返回給TCP客戶端。這個例子類似于我們在做單片機(jī)的串口實驗時,串口上位機(jī)往我們的單片機(jī)發(fā)送數(shù)據(jù),單片機(jī)收到數(shù)據(jù)則把該數(shù)據(jù)原樣返回給上位機(jī)。
tcp_server.c:
#include <stdio.h> #include <winsock2.h>#define BUF_LEN 100int main(void) {WSADATA wd;SOCKET ServerSock, ClientSock;char Buf[BUF_LEN] = {0};SOCKADDR ClientAddr;SOCKADDR_IN ServerSockAddr;int addr_size = 0, recv_len = 0;/* 初始化操作sock需要的DLL */WSAStartup(MAKEWORD(2,2),&wd); /* 創(chuàng)建服務(wù)端socket */if (-1 == (ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))){printf("socket error!\n");exit(1);}/* 設(shè)置服務(wù)端信息 */memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); // 給結(jié)構(gòu)體ServerSockAddr清零ServerSockAddr.sin_family = AF_INET; // 使用IPv4地址ServerSockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");// 本機(jī)IP地址ServerSockAddr.sin_port = htons(1314); // 端口/* 綁定套接字 */if (-1 == bind(ServerSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR))){printf("bind error!\n");exit(1);}/* 進(jìn)入監(jiān)聽狀態(tài) */if (-1 == listen(ServerSock, 10)){printf("listen error!\n");exit(1);}addr_size = sizeof(SOCKADDR);while (1){/* 監(jiān)聽客戶端請求,accept函數(shù)返回一個新的套接字,發(fā)送和接收都是用這個套接字 */if (-1 == (ClientSock = accept(ServerSock, (SOCKADDR*)&ClientAddr, &addr_size))){printf("socket error!\n");exit(1);}/* 接受客戶端的返回數(shù)據(jù) */int recv_len = recv(ClientSock, Buf, BUF_LEN, 0);printf("客戶端發(fā)送過來的數(shù)據(jù)為:%s\n", Buf);/* 發(fā)送數(shù)據(jù)到客戶端 */send(ClientSock, Buf, recv_len, 0);/* 關(guān)閉客戶端套接字 */closesocket(ClientSock);/* 清空緩沖區(qū) */memset(Buf, 0, BUF_LEN); }/*如果有退出循環(huán)的條件,這里還需要清除對socket庫的使用*//* 關(guān)閉服務(wù)端套接字 *///closesocket(ServerSock);/* WSACleanup();*/return 0; }客戶端程序tcp_client.c
#include <stdio.h> #include <winsock2.h>#define BUF_LEN 100int main(void) {WSADATA wd;SOCKET ClientSock;char Buf[BUF_LEN] = {0};SOCKADDR_IN ServerSockAddr;/* 初始化操作sock需要的DLL */WSAStartup(MAKEWORD(2,2),&wd); /* 向服務(wù)器發(fā)起請求 */memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); ServerSockAddr.sin_family = AF_INET;ServerSockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");ServerSockAddr.sin_port = htons(1314);while (1){/* 創(chuàng)建客戶端socket */if (-1 == (ClientSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))){printf("socket error!\n");exit(1);}if (-1 == connect(ClientSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR))){printf("connect error!\n");exit(1);}printf("請輸入一個字符串,發(fā)送給服務(wù)端:");gets(Buf);/* 發(fā)送數(shù)據(jù)到服務(wù)端 */send(ClientSock, Buf, strlen(Buf), 0);/* 接受服務(wù)端的返回數(shù)據(jù) */recv(ClientSock, Buf, BUF_LEN, 0);printf("服務(wù)端發(fā)送過來的數(shù)據(jù)為:%s\n", Buf);memset(Buf, 0, BUF_LEN); // 重置緩沖區(qū)closesocket(ClientSock); // 關(guān)閉套接字} // WSACleanup(); /*如果有退出循環(huán)的條件,這里還需要清除對socket庫的使用*/return 0; }下載mingw,并加入環(huán)境變量。
上述兩個程序放在在bin目錄下
輸入
gcc tcp_server.c -o tcp_server.exe -lwsock32
gcc tcp_client.c -o tcp_client.exe -lwsock32
雙擊exe文件,一開始出現(xiàn)了亂碼:
在notepad中修改編碼:
使用ansi編碼
重新gcc編譯:
再次打開exe,可以正常發(fā)送數(shù)據(jù),亂碼沒了:
(本部分windows下基于tcp的通信代碼來自公眾號:
https://mp.weixin.qq.com/s/C895Oj15jXam10xz3Z_6_w)
測試的圖片是在自己電腦上測試的。
InetAddress 類的方法
這個類表示互聯(lián)網(wǎng)協(xié)議(IP)地址。下面列出了 Socket 編程時比較有用的方法:
1 static InetAddress getByAddress(byte[] addr)
在給定原始 IP 地址的情況下,返回 InetAddress 對象。
2 static InetAddress getByAddress(String host, byte[] addr)
根據(jù)提供的主機(jī)名和 IP 地址創(chuàng)建 InetAddress。
3 static InetAddress getByName(String host)
在給定主機(jī)名的情況下確定主機(jī)的 IP 地址。
4 String getHostAddress()
返回 IP 地址字符串(以文本表現(xiàn)形式)。
5 String getHostName()
獲取此 IP 地址的主機(jī)名。
附錄1 TCP UDP
TCP協(xié)議
TCP(Transmission Control Protocol 傳輸控制協(xié)議)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,數(shù)據(jù)可以準(zhǔn)確發(fā)送,數(shù)據(jù)丟失會重發(fā)。TCP協(xié)議常用于web應(yīng)用中。TCP連接(三次握手)
TCP傳輸起始時,客戶端、服務(wù)端要完成三次數(shù)據(jù)交互工作才能建立連接,常稱為三次握手。
UDP協(xié)議
UDP(User Datagram Protocol, 用戶數(shù)據(jù)報協(xié)議)是一種無連接的傳輸層協(xié)議,提供面向事務(wù)的簡單不可靠信息傳送服務(wù),可以保證通訊效率,傳輸延時小。例如視頻聊天應(yīng)用中用的就是UDP協(xié)議,這樣可以保證及時丟失少量數(shù)據(jù),視頻的顯示也不受很大影響。
附錄2 websocket
很多網(wǎng)站為了實現(xiàn)推送技術(shù),所用的技術(shù)都是 Ajax 輪詢。輪詢是在特定的的時間間隔(如每1秒),由瀏覽器對服務(wù)器發(fā)出HTTP請求,然后由服務(wù)器返回最新的數(shù)據(jù)給客戶端的瀏覽器。這種傳統(tǒng)的模式帶來很明顯的缺點,即瀏覽器需要不斷的向服務(wù)器發(fā)出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數(shù)據(jù)可能只是很小的一部分,顯然這樣會浪費很多的帶寬等資源。
HTML5 定義的 WebSocket 協(xié)議,能更好的節(jié)省服務(wù)器資源和帶寬,并且能夠更實時地進(jìn)行通訊。
瀏覽器通過 JavaScript 向服務(wù)器發(fā)出建立 WebSocket 連接的請求,連接建立以后,客戶端和服務(wù)器端就可以通過 TCP 連接直接交換數(shù)據(jù)。
當(dāng)你獲取 Web Socket 連接后,你可以通過 send() 方法來向服務(wù)器發(fā)送數(shù)據(jù),并通過 onmessage 事件來接收服務(wù)器返回的數(shù)據(jù)。
以下 API 用于創(chuàng)建 WebSocket 對象。
var Socket = new WebSocket(url, [protocol] );
WebSocket
WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議。它實現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信——允許服務(wù)器主動發(fā)送信息給客戶端。 WebSocket通信協(xié)議于2011年被IETF定為標(biāo)準(zhǔn)RFC 6455,并被RFC7936所補(bǔ)充規(guī)范。
優(yōu)點
服務(wù)器可以主動傳送數(shù)據(jù)給客戶端
功能
實現(xiàn)了瀏覽器與服務(wù)器全雙工通信
WebSocket協(xié)議支持(在受控環(huán)境中運(yùn)行不受信任的代碼的)客戶端與(選擇加入該代碼的通信的)遠(yuǎn)程主機(jī)之間進(jìn)行全雙工通信。用于此的安全模型是Web瀏覽器常用的基于原始的安全模式。 協(xié)議包括一個開放的握手以及隨后的TCP層上的消息幀。 該技術(shù)的目標(biāo)是為基于瀏覽器的、需要和服務(wù)器進(jìn)行雙向通信的(服務(wù)器不能依賴于打開多個HTTP連接(例如,使用XMLHttpRequest或和長輪詢))應(yīng)用程序提供一種通信機(jī)制。
產(chǎn)生背景
簡單的說,WebSocket協(xié)議之前,雙工通信是通過多個http鏈接來實現(xiàn),這導(dǎo)致了效率低下。WebSocket解決了這個問題。
長久以來, 創(chuàng)建實現(xiàn)客戶端和用戶端之間雙工通訊的web app都會造成HTTP輪詢的濫用: 客戶端向主機(jī)不斷發(fā)送不同的HTTP呼叫來進(jìn)行詢問。
這會導(dǎo)致一系列的問題:
1.服務(wù)器被迫為每個客戶端使用許多不同的底層TCP連接:一個用于向客戶端發(fā)送信息,其它用于接收每個傳入消息。
2.有些協(xié)議有很高的開銷,每一個客戶端和服務(wù)器之間都有HTTP頭。
3.客戶端腳本被迫維護(hù)從傳出連接到傳入連接的映射來追蹤回復(fù)。
一個更簡單的解決方案是使用單個TCP連接雙向通信。 這就是WebSocket協(xié)議所提供的功能。 結(jié)合WebSocket API ,WebSocket協(xié)議提供了一個用來替代HTTP輪詢實現(xiàn)網(wǎng)頁到遠(yuǎn)程主機(jī)的雙向通信的方法。
WebSocket協(xié)議被設(shè)計來取代用HTTP作為傳輸層的雙向通訊技術(shù),這些技術(shù)只能犧牲效率和可依賴性其中一方來提高另一方,因為HTTP最初的目的不是為了雙向通訊。(獲得更多關(guān)于此的討論可查閱RFC6202)
實現(xiàn)原理
在實現(xiàn)websocket連線過程中,需要通過瀏覽器發(fā)出websocket連線請求,然后服務(wù)器發(fā)出回應(yīng),這個過程通常稱為“握手” 。在 WebSocket API,瀏覽器和服務(wù)器只需要做一個握手的動作,然后,瀏覽器和服務(wù)器之間就形成了一條快速通道。兩者之間就直接可以數(shù)據(jù)互相傳送。在此WebSocket 協(xié)議中,為我們實現(xiàn)即時服務(wù)帶來了兩大好處:
Header
互相溝通的Header是很小的-大概只有 2 Bytes
Server Push
服務(wù)器的推送,服務(wù)器不再被動的接收到瀏覽器的請求之后才返回數(shù)據(jù),而是在有新數(shù)據(jù)時就主動推送給瀏覽器。
WebSocket 事件
以下是 WebSocket 對象的相關(guān)事件。假定我們使用了以上代碼創(chuàng)建了 Socket 對象:
事件 事件處理程序 描述
open Socket.onopen 連接建立時觸發(fā)
message Socket.onmessage 客戶端接收服務(wù)端數(shù)據(jù)時觸發(fā)
error Socket.onerror 通信發(fā)生錯誤時觸發(fā)
close Socket.onclose 連接關(guān)閉時觸發(fā)
方法
Socket.send()
使用連接發(fā)送數(shù)據(jù)
Socket.close()
關(guān)閉連接
為了建立一個 WebSocket 連接,客戶端瀏覽器首先要向服務(wù)器發(fā)起一個 HTTP 請求,這個請求和通常的 HTTP 請求不同,包含了一些附加頭信息,其中附加頭信息"Upgrade: WebSocket"表明這是一個申請協(xié)議升級的 HTTP 請求,服務(wù)器端解析這些附加的頭信息然后產(chǎn)生應(yīng)答信息返回給客戶端,客戶端和服務(wù)器端的 WebSocket 連接就建立起來了,雙方就可以通過這個連接通道自由的傳遞信息,并且這個連接會持續(xù)存在直到客戶端或者服務(wù)器端的某一方主動的關(guān)閉連接。
摘錄自:
https://www.runoob.com/html/html5-websocket.html
總結(jié)
以上是生活随笔為你收集整理的【学习笔记】在windows下进行基于TCP的本地客户端和服务端socket通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【学习笔记】mongoDB初步(一)Mo
- 下一篇: 【学习笔记】mongodb的使用(二)f