您好,登錄后才能下訂單哦!
小編給大家分享一下用TCP協(xié)議實(shí)現(xiàn)客戶端與服務(wù)器通信的方法,希望大家閱讀完這篇文章后大所收獲,下面讓我們一起去探討吧!
進(jìn)行TCP協(xié)議網(wǎng)絡(luò)程序的編寫(xiě),關(guān)鍵在于ServerSocket套接字的熟練使用,TCP通信中所有的信息傳輸都是依托ServerSocket類(lèi)的輸入輸出流進(jìn)行的。
上一篇博客和大家分享了在網(wǎng)絡(luò)編程中要注意的基礎(chǔ)知識(shí),關(guān)于IP、TCP、UDP以及端口和套接字的一些概念,想了解的小伙伴可以看我的這篇文章“盤(pán)點(diǎn)那些進(jìn)行網(wǎng)絡(luò)編程必須要知道的基礎(chǔ)知識(shí)”,那么今天大灰狼就來(lái)和大家分享一下如何使用TCP/IP進(jìn)行網(wǎng)絡(luò)程序的開(kāi)發(fā)。
TCP協(xié)議概念
先來(lái)了解一下TCP協(xié)議的基本概念。
我們知道TCP是可靠而非安全的網(wǎng)絡(luò)協(xié)議。它可以保證數(shù)據(jù)在從一端送至另一端的時(shí)候可以準(zhǔn)確的送達(dá),并且抵達(dá)的數(shù)據(jù)的排列順序和送出時(shí)的順序是相同的。因此在進(jìn)行TCP協(xié)議通信的時(shí)候,我們首先應(yīng)該保證客戶端和服務(wù)器之間的連接通暢。
而TCP協(xié)議程序的編寫(xiě),仍然是依靠套接字Socket類(lèi)來(lái)實(shí)現(xiàn)的,并且利用TCP協(xié)議進(jìn)行通信的兩個(gè)程序之間是有主次之分的,即一個(gè)是服務(wù)器的程序,另一個(gè)是客戶端的程序。因此兩者的功能和編寫(xiě)上也略有不同。如下圖是服務(wù)器與客戶端之間進(jìn)行通信的示意圖:
以上就是在TCP協(xié)議中客戶端與服務(wù)器建立連接的過(guò)程示意圖。而在這其中起到關(guān)鍵作用的就是服務(wù)器端套接字ServerSocket和客戶端套接字Socket。通過(guò)這兩個(gè)套接字來(lái)建立服務(wù)器和客戶端,從而利用其中的函數(shù)進(jìn)行數(shù)據(jù)的通信。
在ServerSocket類(lèi)中有很多需要注意的地方,接下來(lái)大灰狼和大家分享一下ServerSocket類(lèi)的具體用法:
ServerSocket類(lèi)
ServerSocket類(lèi)存在于Java.net包中,表示服務(wù)器端的套接字,在使用時(shí)需要首先導(dǎo)入這個(gè)類(lèi),我們也知道ServerSocket類(lèi)的主要功能就是通過(guò)指定的端口等待來(lái)自于網(wǎng)絡(luò)中客戶端的請(qǐng)求并且進(jìn)行連接。
值得注意的是:服務(wù)器套接字一次只能與一個(gè)客戶端套接字進(jìn)行連接,因此如果存在多臺(tái)客戶端同時(shí)發(fā)送連接請(qǐng)求,則服務(wù)器套接字就會(huì)將請(qǐng)求的客戶端存放到隊(duì)列中去,然后從中取出一個(gè)套接字與服務(wù)器建立的套接字進(jìn)行連接,但是服務(wù)器端能夠容納的客戶端套接字也不是無(wú)限的,當(dāng)請(qǐng)求連接的數(shù)量大于最大容納量時(shí),那么多出來(lái)的請(qǐng)求就會(huì)被拒接,一般來(lái)說(shuō)隊(duì)列的默認(rèn)大小是50。
以下是ServerSocket類(lèi)中一些常用的方法:
ServerSocket類(lèi)中常用的方法
方法 | 返回值 | 說(shuō)明 |
accept() | Socket | 等待客戶機(jī)連接,若連接則創(chuàng)建一個(gè)客戶端套接字 |
isBound() | boolean | 判斷ServerSocket的綁定狀態(tài) |
getInetAddress() | InetAddress | 返回此服務(wù)器套接字的本地地址 |
isClosed() | boolean | 返回服務(wù)器套接字的關(guān)閉狀態(tài) |
close() | void | 關(guān)閉服務(wù)器套接字 |
bind(SocketAddress endpoint) | void | 將ServerSocket綁定到特定地址(IP地址和端口號(hào)) |
getInetAddress() | int | 返回服務(wù)器套接字等待的端口號(hào) |
了解了ServerSocket類(lèi)的基本方法之后,就是如何進(jìn)行客戶端和服務(wù)器進(jìn)行連接的問(wèn)題了。
在服務(wù)器端我們可以調(diào)用ServerSocket類(lèi)的accpet()方法與請(qǐng)求連接的客戶機(jī)建立連接,這時(shí)會(huì)返回一個(gè)和客戶端相連接的Socket對(duì)象,這個(gè)時(shí)候其實(shí)已經(jīng)連接成功了,使用getInetAddress()方法就可以獲取到進(jìn)行請(qǐng)求的客戶機(jī)的IP地址。
對(duì)于如何進(jìn)行客戶端和服務(wù)器端數(shù)據(jù)的通信,就要用到數(shù)據(jù)的輸入流和輸出流了,服務(wù)器端的Socket對(duì)象使用getOutputStream()方法獲取到的輸出流,將指向客戶端的Socket對(duì)象使用getInputStream()方法獲取到的輸入流。由此就實(shí)現(xiàn)在服務(wù)器向客戶端發(fā)送數(shù)據(jù)的一個(gè)過(guò)程,同樣的道理,客戶端端的Socket對(duì)象使用getOutputStream()方法獲取到的輸出流,將指向服務(wù)器端的Socket對(duì)象使用getInputStream()方法獲取到的輸入流。從而實(shí)現(xiàn)由客戶端向服務(wù)器發(fā)送數(shù)據(jù)的過(guò)程。
注意:accpet()方法會(huì)阻塞線程的繼續(xù)執(zhí)行,如果在對(duì)應(yīng)的接口沒(méi)有收到客戶端的呼叫,則程序會(huì)停留在此處,直到獲取到客戶端的呼叫才會(huì)繼續(xù)向下執(zhí)行,但是如果服務(wù)器沒(méi)有收到來(lái)自客戶端的呼叫請(qǐng)求,并且accpet()方法沒(méi)有發(fā)生阻塞,那么通常情況下就是程序出了問(wèn)題,一般來(lái)說(shuō)可能是使用了一個(gè)已經(jīng)被其他程序占用了的端口號(hào),導(dǎo)致ServerSocket沒(méi)有綁定成功!遇到這種情況可以嘗試更換新的端口號(hào)。
了解了TCP協(xié)議的通信過(guò)程,接下來(lái)就是進(jìn)行TCP通信程序的書(shū)寫(xiě)啦!
在網(wǎng)絡(luò)通信中,如果只要求客戶機(jī)向服務(wù)器發(fā)送信息,不要求服務(wù)器向客戶端反饋信息的行為稱(chēng)為“單向通信”,要求客戶機(jī)和服務(wù)器雙方互相通信的過(guò)程稱(chēng)為“雙向通信”,雙向通信只不過(guò)是比單向通信多了一個(gè)服務(wù)器向客戶端發(fā)送消息的過(guò)程,
接下來(lái)分別是服務(wù)器端和客戶端程序的編寫(xiě):
服務(wù)器端程序
package server_1; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class MyTcp { private ServerSocket server; //設(shè)置服務(wù)器套接字 private Socket client; //設(shè)置客戶端套接字 //連接客戶端函數(shù) void getServer() { try { server = new ServerSocket(1100); //建立服務(wù)器 端口為1100 System.out.println("服務(wù)器建立成功!正在等待連接......"); client = server.accept(); //調(diào)用服務(wù)器函數(shù)對(duì)客戶端進(jìn)行連接 System.out.println("客戶端連接成功!ip為:" + client.getInetAddress()); //返回客戶端IP getClientMessage(); //調(diào)用信息傳輸和接收函數(shù) } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } void getClientMessage() { try { while (true) { InputStream is = client.getInputStream(); //獲取到客戶端的輸入流 byte[] b = new byte[1024]; //定義字節(jié)數(shù)組 int len = is.read(b); //由于信息的傳輸是以二進(jìn)制的形式,所以要以二進(jìn)制的形式進(jìn)行數(shù)據(jù)的讀取 String data = new String(b, 0,len); System.out.println("客戶端發(fā)來(lái)消息:" + data); //定義發(fā)送給客戶端的輸出流 OutputStream put = client.getOutputStream(); String putText = "我已經(jīng)收到!歡迎你!"; put.write(putText.getBytes()); //將輸出流信息以二進(jìn)制的形式進(jìn)行寫(xiě)入 } } catch (Exception e) { // TODO: handle exception } try { //判斷客戶端字節(jié)流不是空,則關(guān)閉客戶端 if (server != null) { server.close(); } } catch (Exception e) { // TODO: handle exception } } public static void main(String[] args) { // TODO Auto-generated method stub MyTcp myTcp = new MyTcp(); //調(diào)用該類(lèi)生成對(duì)象 myTcp.getServer(); //調(diào)用方法 } }
客戶端程序
package client_1; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; public class MyClient { private Socket client; //定義客戶端套接字 //建立客戶端函數(shù) void getClient() { try { client = new Socket("127.0.0.1", 1100); //建立客戶端,使用的IP為127.0.0.1,端口和服務(wù)器一樣為1100 System.out.println("客戶端建立成功!"); setClientMessage(); //調(diào)用客戶端信息寫(xiě)入函數(shù) } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //定義客戶端信息寫(xiě)入函數(shù) void setClientMessage() { try { OutputStream pt = client.getOutputStream(); //建立客戶端信息輸出流 String printText = "服務(wù)器你好!我是客戶端!"; pt.write(printText.getBytes()); //以二進(jìn)制的形式將信息進(jìn)行輸出 InputStream input = client.getInputStream(); //建立客戶端信息輸入流 byte [] b = new byte[1024]; //定義字節(jié)數(shù)組 int len = input.read(b); //讀取接收的二進(jìn)制信息流 String data = new String(b, 0,len); System.out.println("收到服務(wù)器消息:" + data); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { //如果客戶端信息流不為空,則說(shuō)明客戶端已經(jīng)建立連接,關(guān)閉客戶端 if (client != null) { client.close(); } } catch (Exception e) { // TODO: handle exception } } public static void main(String[] args) { // TODO Auto-generated method stub //生成客戶端類(lèi)對(duì)象 MyClient myClient = new MyClient(); myClient.getClient(); } }
同時(shí)要注意:在客戶端和服務(wù)器搭建成功之后,應(yīng)該先打開(kāi)服務(wù)器等待連接,再打開(kāi)客戶端進(jìn)行連接,同樣在進(jìn)行關(guān)閉時(shí),應(yīng)該先關(guān)閉客戶端,再關(guān)閉服務(wù)器。
以上面程序?yàn)槔?/p>
打開(kāi)服務(wù)器等待客戶端連接
打開(kāi)客戶端與服務(wù)器連接成功,并且實(shí)現(xiàn)雙向通信:
注意:當(dāng)一臺(tái)機(jī)器上安裝了多個(gè)網(wǎng)絡(luò)應(yīng)用程序時(shí),很可能指定的端口已經(jīng)被占用,甚至還可能遇到之前運(yùn)行很好的程序突然卡住的情況,這種情況很可能是端口被別的程序占用了,這時(shí)可以運(yùn)行netstat-help來(lái)活的幫助,可以使用命令netstat-an來(lái)查看該程序所使用的端口。
看完了這篇文章,相信你對(duì)用TCP協(xié)議實(shí)現(xiàn)客戶端與服務(wù)器通信的方法有了一定的了解,想了解更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。