一、網(wǎng)絡(luò)編程概述
- 網(wǎng)絡(luò)協(xié)議
- 通過計(jì)算機(jī)網(wǎng)絡(luò)可以使多臺計(jì)算機(jī)實(shí)現(xiàn)連接拌屏,位于同一個網(wǎng)絡(luò)中的計(jì)算機(jī)在進(jìn)行連接和通信時需要遵守一定的規(guī)則被廓,這就好比在道路中行駛的汽車一定要遵守交通規(guī)則一樣。在計(jì)算機(jī)網(wǎng)絡(luò)中谅海,這些連接和通信的規(guī)則被稱為網(wǎng)絡(luò)通信協(xié)議脸哀,它對數(shù)據(jù)的傳輸格式、傳輸速率扭吁、傳輸步驟等做了統(tǒng)一規(guī)定撞蜂,通信雙方必須同時遵守才能完成數(shù)據(jù)交換。
- 網(wǎng)絡(luò)通信協(xié)議有很多種侥袜,目前應(yīng)用最廣泛的是TCP/IP協(xié)議(Transmission Control Protocal/Internet Protoal傳輸控制協(xié)議/英特網(wǎng)互聯(lián)協(xié)議)蝌诡,它是一個包括TCP協(xié)議和IP協(xié)議,UDP(User Datagram Protocol)協(xié)議和其它一些協(xié)議的協(xié)議組枫吧,在學(xué)習(xí)具體協(xié)議之前首先了解一下TCP/IP協(xié)議組的層次結(jié)構(gòu)浦旱。
在進(jìn)行數(shù)據(jù)傳輸時,要求發(fā)送的數(shù)據(jù)與收到的數(shù)據(jù)完全一樣九杂,這時颁湖,就需要在原有的數(shù)據(jù)上添加很多信息,以保證數(shù)據(jù)在傳輸過程中數(shù)據(jù)格式完全一致例隆。TCP/IP協(xié)議的層次結(jié)構(gòu)比較簡單爷狈,共分為四層,如圖所示裳擎。
上圖中涎永,TCP/IP協(xié)議中的四層分別是應(yīng)用層、傳輸層鹿响、網(wǎng)絡(luò)層和鏈路層羡微,每層分別負(fù)責(zé)不同的通信功能,接下來針對這四層進(jìn)行詳細(xì)地講解惶我。
- 鏈路層:鏈路層是用于定義物理傳輸通道妈倔,通常是對某些網(wǎng)絡(luò)連接設(shè)備的驅(qū)動協(xié)議,例如針對光纖绸贡、網(wǎng)線提供的驅(qū)動盯蝴。
- 網(wǎng)絡(luò)層:網(wǎng)絡(luò)層是整個TCP/IP協(xié)議的核心,它主要用于將傳輸?shù)臄?shù)據(jù)進(jìn)行分組听怕,將分組數(shù)據(jù)發(fā)送到目標(biāo)計(jì)算機(jī)或者網(wǎng)絡(luò)捧挺。
- 傳輸層:主要使網(wǎng)絡(luò)程序進(jìn)行通信,在進(jìn)行網(wǎng)絡(luò)通信時,可以采用TCP協(xié)議,也可以采用UDP協(xié)議诚些。
- 應(yīng)用層:主要負(fù)責(zé)應(yīng)用程序的協(xié)議邓尤,例如HTTP協(xié)議咽块、FTP協(xié)議等演训。
- IP地址和端口號
- 要想使網(wǎng)絡(luò)中的計(jì)算機(jī)能夠進(jìn)行通信庙楚,必須為每臺計(jì)算機(jī)指定一個標(biāo)識號失受,通過這個標(biāo)識號來指定接受數(shù)據(jù)的計(jì)算機(jī)或者發(fā)送數(shù)據(jù)的計(jì)算機(jī)很魂。
- 在TCP/IP協(xié)議中扎酷,這個標(biāo)識號就是IP地址,它可以唯一標(biāo)識一臺計(jì)算機(jī)遏匆,目前霞玄,IP地址廣泛使用的版本是IPv4,它是由4個字節(jié)大小的二進(jìn)制數(shù)來表示拉岁,如:00001010000000000000000000000001坷剧。由于二進(jìn)制形式表示的IP地址非常不便記憶和處理,因此通常會將IP地址寫成十進(jìn)制的形式喊暖,每個字節(jié)用一個十進(jìn)制數(shù)字(0-255)表示惫企,數(shù)字間用符號“.”分開,如 “192.168.1.100”陵叽。
- 隨著計(jì)算機(jī)網(wǎng)絡(luò)規(guī)模的不斷擴(kuò)大狞尔,對IP地址的需求也越來越多,IPV4這種用4個字節(jié)表示的IP地址面臨枯竭巩掺,因此IPv6 便應(yīng)運(yùn)而生了偏序,IPv6使用16個字節(jié)表示IP地址,它所擁有的地址容量約是IPv4的8×1028倍胖替,達(dá)到2128個(算上全零的)研儒,這樣就解決了網(wǎng)絡(luò)地址資源數(shù)量不夠的問題。
- 通過IP地址可以連接到指定計(jì)算機(jī)独令,但如果想訪問目標(biāo)計(jì)算機(jī)中的某個應(yīng)用程序端朵,還需要指定端口號。在計(jì)算機(jī)中燃箭,不同的應(yīng)用程序是通過端口號區(qū)分的冲呢。端口號是用兩個字節(jié)(16位的二進(jìn)制數(shù))表示的,它的取值范圍是0到65535招狸,其中敬拓,0到1023之間的端口號用于一些知名的網(wǎng)絡(luò)服務(wù)和應(yīng)用,用戶的普通應(yīng)用程序需要使用1024以上的端口號裙戏,從而避免端口號被另外一個應(yīng)用或服務(wù)所占用乘凸。
接下來通過一個圖例來描述IP地址和端口號的作用,如下圖所示挽懦。
從上圖中可以清楚地看到翰意,位于網(wǎng)絡(luò)中一臺計(jì)算機(jī)可以通過IP地址去訪問另一臺計(jì)算機(jī),并通過端口號訪問目標(biāo)計(jì)算機(jī)中的某個應(yīng)用程序信柿。
- InetAddress
了解了IP地址的作用冀偶,我們看學(xué)習(xí)下JDK中提供了一個InetAdderss類,該類用于封裝一個IP地址渔嚷,并提供了一系列與IP地址相關(guān)的方法进鸠,下表中列出了InetAddress類的一些常用方法。
- 案例代碼一:
package com.neuedu.demo; import java.net.InetAddress; import java.net.UnknownHostException; /* * InetAddress:此類表示互聯(lián)網(wǎng)協(xié)議 (IP) 地址形病。 * */ public class InetAddressDemo { public static void main(String[] args) throws UnknownHostException { //static InetAddress getByName(String host) //InetAddress address = InetAddress.getByName("itheima"); InetAddress address = InetAddress.getByName("192.168.1.107");//ip地址是唯一的 //System.out.println(address);//itheima/192.168.1.107 ipconfig String hostAddress = address.getHostAddress();//192.168.1.107 返回IP地址 String hostName = address.getHostName();//itheima 返回主機(jī)名 System.out.println(hostAddress); System.out.println(hostName); } }
二客年、UDP協(xié)議
- UDP是無連接通信協(xié)議,即在數(shù)據(jù)傳輸時漠吻,數(shù)據(jù)的發(fā)送端和接收端不建立邏輯連接量瓜。簡單來說,當(dāng)一臺計(jì)算機(jī)向另外一臺計(jì)算機(jī)發(fā)送數(shù)據(jù)時途乃,發(fā)送端不會確認(rèn)接收端是否存在绍傲,就會發(fā)出數(shù)據(jù),同樣接收端在收到數(shù)據(jù)時耍共,也不會向發(fā)送端反饋是否收到數(shù)據(jù)烫饼。
- 由于使用UDP協(xié)議消耗資源小,通信效率高试读,所以通常都會用于音頻杠纵、視頻和普通數(shù)據(jù)的傳輸例如視頻會議都使用UDP協(xié)議,因?yàn)檫@種情況即使偶爾丟失一兩個數(shù)據(jù)包钩骇,也不會對接收結(jié)果產(chǎn)生太大影響比藻。
但是在使用UDP協(xié)議傳送數(shù)據(jù)時,由于UDP的面向無連接性倘屹,不能保證數(shù)據(jù)的完整性韩容,因此在傳輸重要數(shù)據(jù)時不建議使用UDP協(xié)議。UDP的交換過程如下圖所示唐瀑。
- DatagramPacket
- 前面介紹了UDP是一種面向無連接的協(xié)議群凶,因此,在通信時發(fā)送端和接收端不用建立連接哄辣。UDP通信的過程就像是貨運(yùn)公司在兩個碼頭間發(fā)送貨物一樣请梢。在碼頭發(fā)送和接收貨物時都需要使用集裝箱來裝載貨物,UDP通信也是一樣力穗,發(fā)送和接收的數(shù)據(jù)也需要使用“集裝箱”進(jìn)行打包毅弧,為此JDK中提供了一個DatagramPacket類,該類的實(shí)例對象就相當(dāng)于一個集裝箱当窗,用于封裝UDP通信中發(fā)送或者接收的數(shù)據(jù)够坐。
- 想要創(chuàng)建一個DatagramPacket對象,首先需要了解一下它的構(gòu)造方法。在創(chuàng)建發(fā)送端和接收端的DatagramPacket對象時元咙,使用的構(gòu)造方法有所不同梯影,接收端的構(gòu)造方法只需要接收一個字節(jié)數(shù)組來存放接收到的數(shù)據(jù),而發(fā)送端的構(gòu)造方法不但要接收存放了發(fā)送數(shù)據(jù)的字節(jié)數(shù)組庶香,還需要指定發(fā)送端IP地址和端口號甲棍。
接下來根據(jù)API文檔的內(nèi)容,對DatagramPacket的構(gòu)造方法進(jìn)行逐一詳細(xì)地講解赶掖。
使用該構(gòu)造方法在創(chuàng)建DatagramPacket對象時感猛,指定了封裝數(shù)據(jù)的字節(jié)數(shù)組和數(shù)據(jù)的大小,沒有指定IP地址和端口號奢赂。很明顯陪白,這樣的對象只能用于接收端,不能用于發(fā)送端膳灶。因?yàn)榘l(fā)送端一定要明確指出數(shù)據(jù)的目的地(ip地址和端口號)咱士,而接收端不需要明確知道數(shù)據(jù)的來源,只需要接收到數(shù)據(jù)即可袖瞻。
使用該構(gòu)造方法在創(chuàng)建DatagramPacket對象時司致,不僅指定了封裝數(shù)據(jù)的字節(jié)數(shù)組和數(shù)據(jù)的大小,還指定了數(shù)據(jù)包的目標(biāo)IP地址(address)和端口號(port)聋迎。該對象通常用于發(fā)送端脂矫,因?yàn)樵诎l(fā)送數(shù)據(jù)時必須指定接收端的IP地址和端口號,就好像發(fā)送貨物的集裝箱上面必須標(biāo)明接收人的地址一樣霉晕。
上面我們講解了DatagramPacket的構(gòu)造方法庭再,接下來對DatagramPacket類中的常用方法進(jìn)行詳細(xì)地講解,如下表所示牺堰。
- DatagramSocket
DatagramPacket數(shù)據(jù)包的作用就如同是“集裝箱”拄轻,可以將發(fā)送端或者接收端的數(shù)據(jù)封裝起來。然而運(yùn)輸貨物只有“集裝箱”是不夠的伟葫,還需要有碼頭恨搓。在程序中需要實(shí)現(xiàn)通信只有DatagramPacket數(shù)據(jù)包也同樣不行,為此JDK中提供的一個DatagramSocket類筏养。DatagramSocket類的作用就類似于碼頭斧抱,使用這個類的實(shí)例對象就可以發(fā)送和接收DatagramPacket數(shù)據(jù)包,發(fā)送數(shù)據(jù)的過程如下圖所示渐溶。
在創(chuàng)建發(fā)送端和接收端的DatagramSocket對象時辉浦,使用的構(gòu)造方法也有所不同,下面對DatagramSocket類中常用的構(gòu)造方法進(jìn)行講解茎辐。
該構(gòu)造方法用于創(chuàng)建發(fā)送端的DatagramSocket對象宪郊,在創(chuàng)建DatagramSocket對象時掂恕,并沒有指定端口號,此時弛槐,系統(tǒng)會分配一個沒有被其它網(wǎng)絡(luò)程序所使用的端口號懊亡。
該構(gòu)造方法既可用于創(chuàng)建接收端的DatagramSocket對象,又可以創(chuàng)建發(fā)送端的DatagramSocket對象丐黄,在創(chuàng)建接收端的DatagramSocket對象時斋配,必須要指定一個端口號孔飒,這樣就可以監(jiān)聽指定的端口灌闺。- 上面我們講解了DatagramSocket的構(gòu)造方法,接下來對DatagramSocket類中的常用方法進(jìn)行詳細(xì)地講解坏瞄。
- UDP實(shí)現(xiàn)
- 案例代碼二:
package com.neuedu.demo; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; /* * 使用UDP協(xié)議發(fā)送數(shù)據(jù) * 創(chuàng)建發(fā)送端Socket對象 * 創(chuàng)建數(shù)據(jù)并打包 * 發(fā)送數(shù)據(jù) * 釋放資源 * * DatagramSocket:此類表示用來發(fā)送和接收數(shù)據(jù),基于UDP協(xié)議的 * * DatagramSocket() :創(chuàng)建Socket對象并隨機(jī)分配端口號 * DatagramSocket(int port) :創(chuàng)建Socket對象并指定端口號 */ public class SendDemo { public static void main(String[] args) throws IOException { //創(chuàng)建發(fā)送端Socket對象 DatagramSocket ds = new DatagramSocket(); //創(chuàng)建數(shù)據(jù)并打包 /* * DatagramPacket :此類表示數(shù)據(jù)報包 * 數(shù)據(jù) byte[] * 設(shè)備的地址 ip * 進(jìn)程的地址 端口號 DatagramPacket(byte[] buf, int length, InetAddress address, int port) */ String s = "hello udp,im comming!"; byte[] bys = s.getBytes(); int length = bys.length; InetAddress address = InetAddress.getByName("itheima");//發(fā)送給當(dāng)前設(shè)備 int port = 8888; //打包 DatagramPacket dp = new DatagramPacket(bys,length,address,port); //發(fā)送數(shù)據(jù) ds.send(dp); //釋放資源 ds.close(); } }
package com.neuedu.demo; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /* * 使用UDP協(xié)議接收數(shù)據(jù) * 創(chuàng)建接收端Socket對象 * 接收數(shù)據(jù) * 解析數(shù)據(jù) * 輸出數(shù)據(jù) * 釋放資源 * */ public class ReceiveDemo { public static void main(String[] args) throws IOException { //創(chuàng)建接收端Socket對象 DatagramSocket ds = new DatagramSocket(8888); //接收數(shù)據(jù) //DatagramPacket(byte[] buf, int length) byte[] bys = new byte[1024]; DatagramPacket dp = new DatagramPacket(bys,bys.length); System.out.println(1); ds.receive(dp);//阻塞 System.out.println(2); //解析數(shù)據(jù) //InetAddress getAddress() : 獲取發(fā)送端的IP對象 InetAddress address = dp.getAddress(); //byte[] getData() :獲取接收到的數(shù)據(jù)桂对,也可以直接使用創(chuàng)建包對象時的數(shù)組 byte[] data = dp.getData(); //int getLength() :獲取具體收到數(shù)據(jù)的長度 int length = dp.getLength(); //輸出數(shù)據(jù) System.out.println("sender ---> " + address.getHostAddress()); //System.out.println(new String(data,0,length)); System.out.println(new String(bys,0,length)); //釋放資源 ds.close(); } }
三、TCP協(xié)議
- TCP通信同UDP通信一樣鸠匀,都能實(shí)現(xiàn)兩臺計(jì)算機(jī)之間的通信蕉斜,通信的兩端都需要創(chuàng)建socket對象。
- 區(qū)別在于缀棍,UDP中只有發(fā)送端和接收端宅此,不區(qū)分客戶端與服務(wù)器端,計(jì)算機(jī)之間可以任意地發(fā)送數(shù)據(jù)爬范。
- 而TCP通信是嚴(yán)格區(qū)分客戶端與服務(wù)器端的父腕,在通信時,必須先由客戶端去連接服務(wù)器端才能實(shí)現(xiàn)通信青瀑,服務(wù)器端不可以主動連接客戶端璧亮,并且服務(wù)器端程序需要事先啟動,等待客戶端的連接斥难。
- 在JDK中提供了兩個類用于實(shí)現(xiàn)TCP程序枝嘶,一個是ServerSocket類,用于表示服務(wù)器端哑诊,一個是Socket類群扶,用于表示客戶端。
- 通信時镀裤,首先創(chuàng)建代表服務(wù)器端的ServerSocket對象竞阐,該對象相當(dāng)于開啟一個服務(wù),并等待客戶端的連接淹禾,然后創(chuàng)建代表客戶端的Socket對象向服務(wù)器端發(fā)出連接請求馁菜,服務(wù)器端響應(yīng)請求,兩者建立連接開始通信铃岔。
- ServerSocket
通過前面的學(xué)習(xí)知道汪疮,在開發(fā)TCP程序時峭火,首先需要創(chuàng)建服務(wù)器端程序。JDK的java.net包中提供了一個ServerSocket類智嚷,該類的實(shí)例對象可以實(shí)現(xiàn)一個服務(wù)器段的程序卖丸。通過查閱API文檔可知,ServerSocket類提供了多種構(gòu)造方法盏道,接下來就對ServerSocket的構(gòu)造方法進(jìn)行逐一地講解稍浆。
使用該構(gòu)造方法在創(chuàng)建ServerSocket對象時,就可以將其綁定到一個指定的端口號上(參數(shù)port就是端口號)猜嘱。
接下來學(xué)習(xí)一下ServerSocket的常用方法衅枫,如表所示。
ServerSocket對象負(fù)責(zé)監(jiān)聽某臺計(jì)算機(jī)的某個端口號朗伶,在創(chuàng)建ServerSocket對象后弦撩,需要繼續(xù)調(diào)用該對象的accept()方法,接收來自客戶端的請求论皆。當(dāng)執(zhí)行了accept()方法之后益楼,服務(wù)器端程序會發(fā)生阻塞,直到客戶端發(fā)出連接請求点晴,accept()方法才會返回一個Scoket對象用于和客戶端實(shí)現(xiàn)通信感凤,程序才能繼續(xù)向下執(zhí)行。
- Socket
講解了ServerSocket對象可以實(shí)現(xiàn)服務(wù)端程序粒督,但只實(shí)現(xiàn)服務(wù)器端程序還不能完成通信陪竿,此時還需要一個客戶端程序與之交互,為此JDK提供了一個Socket類坠陈,用于實(shí)現(xiàn)TCP客戶端程序萨惑。
通過查閱API文檔可知Socket類同樣提供了多種構(gòu)造方法,接下來就對Socket的常用構(gòu)造方法進(jìn)行詳細(xì)講解仇矾。
使用該構(gòu)造方法在創(chuàng)建Socket對象時庸蔼,會根據(jù)參數(shù)去連接在指定地址和端口上運(yùn)行的服務(wù)器程序,其中參數(shù)host接收的是一個字符串類型的IP地址贮匕。
該方法在使用上與第二個構(gòu)造方法類似姐仅,參數(shù)address用于接收一個InetAddress類型的對象,該對象用于封裝一個IP地址刻盐。
在以上Socket的構(gòu)造方法中掏膏,最常用的是第一個構(gòu)造方法。
接下來學(xué)習(xí)一下Socket的常用方法敦锌,如表所示
在Socket類的常用方法中馒疹,getInputStream()和getOutStream()方法分別用于獲取輸入流和輸出流。當(dāng)客戶端和服務(wù)端建立連接后乙墙,數(shù)據(jù)是以IO流的形式進(jìn)行交互的颖变,從而實(shí)現(xiàn)通信生均。
接下來通過一張圖來描述服務(wù)器端和客戶端的數(shù)據(jù)傳輸,如下圖所示腥刹。
- TCP協(xié)議實(shí)現(xiàn)
- 案例代碼三:
package com.neuedu.demo; import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; /* * 使用TCP協(xié)議發(fā)送數(shù)據(jù) * 創(chuàng)建發(fā)送端Socket對象(創(chuàng)建連接) * 獲取輸出流對象 * 發(fā)送數(shù)據(jù) * 釋放資源 * * Socket(InetAddress address, int port) * Exception in thread "main" java.net.ConnectException: Connection refused: connect * */ public class ClientDemo { public static void main(String[] args) throws IOException { //創(chuàng)建發(fā)送端Socket對象(創(chuàng)建連接) Socket s = new Socket(InetAddress.getByName("itheima"),10086); //獲取輸出流對象 OutputStream os = s.getOutputStream(); //發(fā)送數(shù)據(jù) String str = "hello tcp,im comming!!!"; os.write(str.getBytes()); //釋放資源 //os.close(); s.close(); } }
package com.neuedu.demo; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /* * 使用TCP協(xié)議接收數(shù)據(jù) * 創(chuàng)建接收端Socket對象 * 監(jiān)聽(阻塞) * 獲取輸入流對象 * 獲取數(shù)據(jù) * 輸出數(shù)據(jù) * 釋放資源 * * ServerSocket:接收端马胧,服務(wù)端Socket * ServerSocket(int port) * Socket accept() * */ public class ServerDemo { public static void main(String[] args) throws IOException { //創(chuàng)建接收端Socket對象 ServerSocket ss = new ServerSocket(10086); //監(jiān)聽(阻塞) Socket s = ss.accept(); //獲取輸入流對象 InputStream is = s.getInputStream(); //獲取數(shù)據(jù) byte[] bys = new byte[1024]; int len;//用于存儲讀到的字節(jié)個數(shù) len = is.read(bys); //輸出數(shù)據(jù) InetAddress address = s.getInetAddress(); System.out.println("client ---> " + address.getHostName()); System.out.println(new String(bys,0,len)); //釋放資源 s.close(); //ss.close(); } }
- TCP相關(guān)案例
- 案例代碼四:
使用TCP協(xié)議發(fā)送數(shù)據(jù),服務(wù)端將接收到的數(shù)據(jù)轉(zhuǎn)換成大寫返回給客戶端package com.neuedu.demo; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; /* * 需求:使用TCP協(xié)議發(fā)送數(shù)據(jù)衔峰,并將接收到的數(shù)據(jù)轉(zhuǎn)換成大寫返回 * * 客戶端發(fā)出數(shù)據(jù) * 服務(wù)端接收數(shù)據(jù) * 服務(wù)端轉(zhuǎn)換數(shù)據(jù) * 服務(wù)端發(fā)出數(shù)據(jù) * 客戶端接收數(shù)據(jù) * */ public class ClientDemo { public static void main(String[] args) throws IOException { //創(chuàng)建客戶端Socket對象 Socket s = new Socket(InetAddress.getByName("itheima"),10010); //獲取輸出流對象 OutputStream os = s.getOutputStream(); //發(fā)出數(shù)據(jù) os.write("tcp,im comming again!!!".getBytes()); //獲取輸入流對象 InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len;//用于存儲讀取到的字節(jié)個數(shù) //接收數(shù)據(jù) len = is.read(bys); //輸出數(shù)據(jù) System.out.println(new String(bys,0,len)); //釋放資源 s.close(); } }
package com.neuedu.demo; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class ServerDemo { public static void main(String[] args) throws IOException { //創(chuàng)建服務(wù)端Socket對象 ServerSocket ss = new ServerSocket(10010); //監(jiān)聽 Socket s = ss.accept(); //獲取輸入流對象 InputStream is = s.getInputStream(); //獲取數(shù)據(jù) byte[] bys = new byte[1024]; int len;//用于存儲讀取到的字節(jié)個數(shù) len = is.read(bys); String str = new String(bys,0,len); //輸出數(shù)據(jù) System.out.println(str); //轉(zhuǎn)換數(shù)據(jù) String upperStr = str.toUpperCase(); //獲取輸出流對象 OutputStream os = s.getOutputStream(); //返回數(shù)據(jù)(發(fā)出數(shù)據(jù)) os.write(upperStr.getBytes()); //釋放資源 s.close(); //ss.close();//服務(wù)端一般不關(guān)閉 } }
- 案例代碼五:
- 客戶端:
1.提示用戶輸入用戶名和密碼,將用戶輸入的用戶名和密碼發(fā)送給服務(wù)端
2.接收服務(wù)端驗(yàn)證完用戶名和密碼的結(jié)果- 服務(wù)端:
1.接收客戶端發(fā)送過來的用戶名和密碼
2.如果用戶名不是itheima或者 密碼不是123456,就向客戶端寫入”登錄失敗”
否則向客戶端寫入登錄成功
package com.neuedu.demo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; /* * 模擬用戶登錄 */ public class ClientTest { public static void main(String[] args) throws IOException { //創(chuàng)建客戶端Socket對象 //Socket s = new Socket(InetAddress.getByName("itheima"),8888); Socket s = new Socket("itheima",8888); //獲取用戶名和密碼 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("請輸入用戶名:"); String username = br.readLine(); System.out.println("請輸入密碼:"); String password = br.readLine(); //獲取輸出流對象 //BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); PrintWriter out = new PrintWriter(s.getOutputStream(),true); //寫出數(shù)據(jù) out.println(username); out.println(password); //獲取輸入流對象 BufferedReader serverBr = new BufferedReader(new InputStreamReader(s.getInputStream())); //獲取服務(wù)器返回的數(shù)據(jù) String result = serverBr.readLine(); System.out.println(result); //釋放資源 s.close(); } }
package com.neuedu.demo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class ServerTest { public static void main(String[] args) throws IOException { //創(chuàng)建服務(wù)器端Socket對象 ServerSocket ss = new ServerSocket(8888); //監(jiān)聽 Socket s = ss.accept(); //獲取輸入流對象 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //獲取用戶名和密碼 String username = br.readLine(); String password = br.readLine(); //判斷用戶名和密碼是否正確 boolean flag = false; if("itheima".equals(username) && "123456".equals(password)) { flag = true; } //獲取輸出流對象 PrintWriter out = new PrintWriter(s.getOutputStream(),true); //返回判斷信息 if(flag) { out.println("登陸成功"); } else { out.println("登陸失敗"); } //釋放資源 s.close(); //ss.close();//服務(wù)器一般不關(guān)閉 } }
- 案例代碼六:
- 將用戶名和密碼封裝到一個User類中,提供對應(yīng)的構(gòu)造方法和getter/setter方法
- 新建一個UserDB類里面定義一個集合,在集合中添加以下User對象
new User("zhangsan","123456");
new User("lisi","654321");
new User("itheima","itheima");
new User("admin","password");- 客戶端:
1.提示用戶輸入用戶名和密碼,將用戶輸入的用戶名和密碼發(fā)送給服務(wù)端
2.接收服務(wù)端驗(yàn)證完用戶名和密碼的結(jié)果- 服務(wù)端:
- 服務(wù)端將客戶端發(fā)送過來的用戶名密碼封裝成User對象
- 集合中如果包括這個User對象,想客戶端寫入” 登錄成功”
否則向客戶端寫入”登錄失敗”
package com.neuedu.demo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; /* * 模擬用戶登錄改寫(面向?qū)ο蟀姹荆?*/ public class ClientTest { public static void main(String[] args) throws IOException { //創(chuàng)建客戶端Socket對象 Socket s = new Socket("itheima",8888); //獲取用戶名和密碼 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("請輸入用戶名:"); String username = br.readLine(); System.out.println("請輸入密碼:"); String password = br.readLine(); //獲取輸出流對象 PrintWriter out = new PrintWriter(s.getOutputStream(),true); //寫出數(shù)據(jù) out.println(username); out.println(password); //獲取輸入流對象 BufferedReader serverBr = new BufferedReader(new InputStreamReader(s.getInputStream())); //獲取服務(wù)器返回的數(shù)據(jù) String result = serverBr.readLine(); System.out.println(result); //釋放資源 s.close(); } }
package com.neuedu.demo; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.List; public class ServerTest { public static void main(String[] args) throws IOException { //創(chuàng)建服務(wù)器端Socket對象 ServerSocket ss = new ServerSocket(8888); //監(jiān)聽 Socket s = ss.accept(); //獲取輸入流對象 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //獲取用戶名和密碼 String username = br.readLine(); String password = br.readLine(); //判斷用戶名和密碼是否正確 boolean flag = false; /*if("itheima".equals(username) && "123456".equals(password)) { flag = true; }*/ List<User> users = UserDB.getUsers(); User user = new User(username,password); if(users.contains(user)) { //匹配成功 flag = true; } //獲取輸出流對象 PrintWriter out = new PrintWriter(s.getOutputStream(),true); //返回判斷信息 if(flag) { out.println("登陸成功"); } else { out.println("登陸失敗"); } //釋放資源 s.close(); //ss.close();//服務(wù)器一般不關(guān)閉 } }