Java如何利用Socket進(jìn)行數(shù)據(jù)讀寫
利用Socket進(jìn)行數(shù)據(jù)讀寫
Java中和網(wǎng)絡(luò)有關(guān)的類分為四種:InetAddress(網(wǎng)絡(luò)信息標(biāo)識(shí))、URL(統(tǒng)一資源定位器,讀寫網(wǎng)絡(luò)數(shù)據(jù))、Sockets(利用TCP/IP實(shí)現(xiàn)網(wǎng)絡(luò)通信)、Datagram(UDP數(shù)據(jù)報(bào)通信)
InetAddress與URL
通過InetAddress可以獲取計(jì)算機(jī)名、IP地址等信息
public static void main(String[] args) throws UnknownHostException { InetAddress address=InetAddress.getLocalHost(); // 通過靜態(tài)方法生成InetAddress實(shí)例 System.out.println("計(jì)算機(jī)名稱:"+ address.getHostName()); System.out.println("IP地址:"+ address.getHostAddress()); byte[] ipBytes=address.getAddress(); // 以數(shù)組的形式獲取IP System.out.println("IP地址數(shù)組:"+ Arrays.toString(ipBytes)); System.out.println(address); //打印實(shí)例,輸出:主機(jī)名/IP InetAddress address1=InetAddress.getByName("DELL-T"); // 通過主機(jī)名獲取實(shí)例 System.out.println(address1.getHostAddress()); InetAddress address2=InetAddress.getByName("192.168.80.1"); //通過IP字符串獲取實(shí)例 System.out.println(address2.getHostName()); }
url用于唯一標(biāo)識(shí)網(wǎng)絡(luò)上的資源位置,由協(xié)議名和資源名兩部分組成,例如https://www.baidu.com/s?wd=Java#p1,https為協(xié)議名,后面為資源名。java.net包中的URL類用于操作url相關(guān)信息,其使用如下:
URL baidu = new URL("https://www.baidu.com"); // 通過網(wǎng)址創(chuàng)建url實(shí)例 URL url = new URL(baidu, "s?wd=Java#p1"); // 在url對(duì)象的基礎(chǔ)上拼接成新的url實(shí)例 //查看url相關(guān)信息 System.out.println("協(xié)議" + url.getProtocol()); System.out.println("主機(jī)" + url.getHost()); System.out.println("端口" + url.getPort()); System.out.println("文件路徑" + url.getFile()); System.out.println("相對(duì)路徑" + url.getRef()); System.out.println("查詢內(nèi)容" + url.getQuery());
結(jié)果如下:
通過url的openStream()方法可以獲取url對(duì)應(yīng)的網(wǎng)絡(luò)資源,打開輸入流之后便可讀取url內(nèi)容
InputStream inputStream = url.openStream(); //打開網(wǎng)絡(luò)資源輸入流 BufferedReader br = new BufferedReader(new InputStreamReader(inputStream)); //緩沖讀取內(nèi)容 String line = br.readLine(); while (line != null) { System.out.println(line); line = br.readLine(); } br.close(); inputStream.close();
Socket通信
Sockets是指TCP協(xié)議的基礎(chǔ)上實(shí)現(xiàn)的面向連接、可靠、有序、面向字節(jié)流的網(wǎng)絡(luò)通信類,主要包括兩個(gè)類,在服務(wù)器端的ServerSocket類和在客戶端的Socket類,其通信流程如下所示
如下所示為客戶端向服務(wù)器發(fā)送登錄請(qǐng)求,服務(wù)器返回相應(yīng)的Socket通信示例過程:
//服務(wù)器端,先啟動(dòng) public class ServerSocketDemo { public static void main(String[] args) { try { //1、創(chuàng)建服務(wù)器端ServerSocket對(duì)象,綁定監(jiān)聽的端口號(hào)6666 ServerSocket serverSocket = new ServerSocket(6666); //2、調(diào)用accept()方法監(jiān)聽開始,等待客戶端連接 System.out.println("服務(wù)器端等待客戶端連接。。。"); Socket socket = serverSocket.accept(); //3、通過輸入流讀取客戶端傳來的信息 InputStream inputStream = socket.getInputStream(); //獲取字節(jié)輸入流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));//包裝為緩沖字符流 String info; while ((info = bufferedReader.readLine()) != null) System.out.println("收到客戶端信息:" + info); InetAddress address = socket.getInetAddress(); // 獲取客戶端的InetAddress信息 System.out.println("當(dāng)前客戶端的IP:" + address.getHostAddress()); socket.shutdownInput(); //關(guān)閉socket輸入流 //4、通過輸出流向客戶端返回相應(yīng)信息 OutputStream outputStream = socket.getOutputStream(); //獲取輸出流 PrintWriter printWriter = new PrintWriter(outputStream); //將輸出流包裝為打印流 printWriter.write("歡迎登錄!"); printWriter.flush(); socket.shutdownOutput(); //關(guān)閉socket輸出流 //5、關(guān)閉資源流 printWriter.close(); outputStream.close(); bufferedReader.close(); inputStream.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
//客戶端 public class ClientSocketDemo { public static void main(String[] args) { try { //1、創(chuàng)建客戶端Socket對(duì)象,指定要連接的服務(wù)器和端口 Socket socket = new Socket("localhost", 6666); //2、通過輸出流向服務(wù)器發(fā)送信息 OutputStream outputStream = socket.getOutputStream(); //獲取輸出流 PrintWriter printWriter = new PrintWriter(outputStream); //將輸出流包裝為打印流 printWriter.write("用戶名:小明;密碼:1234"); printWriter.flush(); socket.shutdownOutput(); //3、通過輸入流接收服務(wù)器的返回信息 InputStream inputStream = socket.getInputStream(); //獲取字節(jié)輸入流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));//包裝為緩沖字符流 String info; while ((info = bufferedReader.readLine()) != null) System.out.println("收到服務(wù)器端相應(yīng):" + info); socket.shutdownInput(); //4、關(guān)閉流資源 bufferedReader.close(); inputStream.close(); printWriter.close(); outputStream.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
UDP通信
UDP是無連接、不可靠、無序的傳輸協(xié)議,但其傳輸速度較快,因此常用于對(duì)速度敏感、不要求太高準(zhǔn)去率的傳輸過程。在傳輸時(shí),首先將要傳輸?shù)臄?shù)據(jù)封裝成數(shù)據(jù)報(bào)(Datagram),在數(shù)據(jù)報(bào)中定義數(shù)據(jù)要目的主機(jī)和端口號(hào)(Socket),然后再將數(shù)據(jù)發(fā)送出去。Java中的DatagramPacket類表示數(shù)據(jù)包,DatagramSocket類表示端到端的UDP通信。如下所示為服務(wù)器端和客戶端通過UDP通信的過程:
//服務(wù)器端 public class UDPServer { public static void main(String[] args) throws IOException { //服務(wù)器端接收客戶端信息 //1、創(chuàng)建非服務(wù)器端的UDPsocket對(duì)象并指定端口 DatagramSocket socket = new DatagramSocket(6666); //2、創(chuàng)建用于接受客戶端數(shù)據(jù)的數(shù)據(jù)報(bào) byte[] data = new byte[1024]; //用于接收數(shù)據(jù)的字節(jié)數(shù)組 DatagramPacket packet = new DatagramPacket(data, data.length); //3、接收客戶端的數(shù)據(jù) System.out.println("等待客戶端數(shù)據(jù)中。。。"); socket.receive(packet); //4、讀取接收在data中的字節(jié)數(shù)組轉(zhuǎn)化為字符串 String s = new String(data, 0, packet.getLength()); System.out.println("服務(wù)器端收到的數(shù)據(jù):" + s); //服務(wù)器向客戶端發(fā)送響應(yīng)信息 //1、定義響應(yīng)信息與客戶端的地址和端口 byte[] response = "歡迎您!".getBytes(); InetAddress clientAddress = packet.getAddress(); //從客戶端發(fā)來的數(shù)據(jù)報(bào)中得到其IP地址 int port = packet.getPort(); //2、創(chuàng)建響應(yīng)數(shù)據(jù)報(bào) DatagramPacket responsePacket = new DatagramPacket(response, response.length, clientAddress, port); //3、發(fā)送數(shù)據(jù)報(bào) socket.send(responsePacket); //4、關(guān)閉資源 socket.close(); } }
//客戶端 public class UDPClient { public static void main(String[] args) throws IOException { //客戶端向服務(wù)器發(fā)送數(shù)據(jù)報(bào) //1、創(chuàng)建數(shù)據(jù)報(bào),包含要發(fā)送的數(shù)據(jù)、目標(biāo)服務(wù)器地址、端口號(hào) byte[] data = "用戶名:小明;密碼:1234".getBytes(); InetAddress address = InetAddress.getByName("localhost"); DatagramPacket packet = new DatagramPacket(data, data.length, address, 6666); //2、創(chuàng)建DatagramSocket對(duì)象 DatagramSocket socket = new DatagramSocket(); //3、發(fā)送數(shù)據(jù)包 socket.send(packet); //客戶端接收服務(wù)器響應(yīng)數(shù)據(jù)報(bào) //1、創(chuàng)建接收數(shù)據(jù)報(bào)的packet byte[] resData = new byte[1024]; DatagramPacket resPacket = new DatagramPacket(resData, resData.length); //2、接收響應(yīng)數(shù)據(jù) socket.receive(resPacket); //3、讀取響應(yīng)數(shù)據(jù) String res = new String(resData, 0, resPacket.getLength()); System.out.println("客戶端收到響應(yīng):" + res); //4、關(guān)閉資源 socket.close(); } }
Socket通信中,持續(xù)單向讀寫的同步問題
在Java網(wǎng)絡(luò)編程中,有的時(shí)候客戶端或者服務(wù)端需要持續(xù)向?qū)Ψ桨l(fā)送數(shù)據(jù),有的時(shí)候發(fā)送速度超過了接收速度,就會(huì)出現(xiàn)一次讀兩份數(shù)據(jù),甚至更多的現(xiàn)象發(fā)生。
如何解決這個(gè)問題呢?
我們可以每次發(fā)送一行數(shù)據(jù),然后另一邊每次讀入一行數(shù)據(jù)。這邊一行一行地發(fā)送,那邊讀完一行了再去讀下一行,這樣就會(huì)每次發(fā)送的數(shù)據(jù)以行為單位,就可以避免一次接受多條連著的數(shù)據(jù)了。因?yàn)榉?wù)端和客戶端都是Socket操作,其實(shí)二者都是一樣的,所以沒有具體區(qū)分服務(wù)端還是客戶端。
發(fā)送端Socket操作(Socket send)
//獲得發(fā)送端socket的OutputStream的PrintWriter封裝對(duì)象 PrintWriter printWriter = new PrintWriter(send.getOutputStream()); //持續(xù)發(fā)送數(shù)據(jù) for (int i = 0; i < 500; i++) { String message = "Hello World"; //發(fā)送一行數(shù)據(jù) printWriter.println(message); //將緩沖區(qū)數(shù)據(jù)發(fā)送出去 printWriter.flush(); }
接收端Socket操作(Socket receive)
//獲得接收端socket的InputStream的BufferedReader封裝對(duì)象 BufferedReader reader=new BufferedReader(new InputStreamReader(receive.getInputStream())); //一直接收數(shù)據(jù) while (true){ String message = reader.readLine(); System.out.println(message); }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
解析ConcurrentHashMap: put方法源碼分析
ConcurrentHashMap是由Segment數(shù)組結(jié)構(gòu)和HashEntry數(shù)組結(jié)構(gòu)組成。Segment的結(jié)構(gòu)和HashMap類似,是一種數(shù)組和鏈表結(jié)構(gòu),今天給大家普及java面試常見問題---ConcurrentHashMap知識(shí),一起看看吧2021-06-06java Volatile與Synchronized的區(qū)別
這篇文章主要介紹了java Volatile與Synchronized的區(qū)別,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-12-12深入了解Java設(shè)計(jì)模式之職責(zé)鏈模式
Java設(shè)計(jì)模式中有很多種類別,例如單例模式、裝飾模式、觀察者模式等。本文將為大家詳細(xì)介紹其中的職責(zé)鏈模式,感興趣的可以了解一下2022-09-09關(guān)于HttpClient 引發(fā)的線程太多導(dǎo)致FullGc的問題
這篇文章主要介紹了關(guān)于HttpClient 引發(fā)的線程太多導(dǎo)致FullGc的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01Java lambda表達(dá)式實(shí)現(xiàn)Flink WordCount過程解析
這篇文章主要介紹了Java lambda表達(dá)式實(shí)現(xiàn)Flink WordCount過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02SpringCloud Gateway 利用 Mysql 實(shí)現(xiàn)動(dòng)態(tài)路由的方法
這篇文章主要介紹了SpringCloud Gateway 利用 Mysql 實(shí)現(xiàn)動(dòng)態(tài)路由的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02