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