黑馬程序員——Java基礎---網絡編程

-----------android培訓java培訓、java學習型技術博客、期待與您交流!------------ 

計算機網絡

是指將地理位置不同的具有獨立功能的多臺計算機及其外部設備,通過通信線路連接起來,在網絡操作系統,網絡管理軟件及網絡通信協議的管理和協調下,實現資源共享和信息傳遞的計算機系統。

 

網絡編程

就是用來實現網絡互連的不同計算機上運行的程序間可以進行數據交換。

網絡模型

主要是研究計算機網絡之間以何種規則進行通信。

網絡模型一般是指

OSIOpen System Interconnection開放系統互連)參考模型

TCP/IP參考模型

 

 

        一般來說開發處於傳輸層和網際層,應用層爲:FTP和HTTP協議等,傳輸層爲:UDP和TCP等,網際層爲:IP。

        通常用戶操作主要是基於應用層,而編程人員操作的是傳輸層和網際層,用戶在應用層操作的數據,經過逐層封包,最後經由物理層通過傳輸設備發送到另一個模型中,再進行逐層解包,最後被用戶使用。如下圖:

 

2、網絡通信三要素:IP地址,端口號,傳輸協議

A、IP地址(InetAddress

        它是網絡中的設備標識,不易記憶,可用主機名錶示,兩者存在映射關係。

        通常本機迴環地址默認爲:127.0.0.1,主機名爲:localhost。

InetAddress類:

位於java.net包中。沒有構造函數,主要的方法有可getLocalHost()方法與InetAddress對象,其作用分別是獲取本機名和本機地址。

     常用方法:

                1)static InetAddress getByName(String host):獲取指定主機的IP和主機名。(最好用ip地址去獲取,主機名需要解析)

                2)static InetAddress[] getAllByName(String host):在給定主機名的情況下,根據系統上配置的名稱服務返回IP地址所組成的數組。返回對象不唯一時,用此方法。

                3)String getHostAddress():返回IP地址字符串文本形式,以IP地址爲主。

                4)String getHostName():返回IP地址主機名。

獲取任意一臺主機的IP地址對象:

                1)功能:返回InetAddress對象

                2)對於任意主機,需要指定傳入主機名的參數

注意:如果IP地址和對應的主機名,這種映射關係沒有在網絡上,就不會解析成功,返回的還是指定的IP。

演示用例:

InetAddress i = InetAddress.getLocalHost();

System.out.println(i.toString);//打印本機的主機名和地址

System.out.println("address:"+i.getHostAddress());//打印本機的地址

System.out.println("name"+i.getHostName);//打印本機的主機名

 

 

InetAddress i = InetAddress.getByName("192.168.1.106");

System.out.println("address:"+i.getHostAddress());//打印本機的地址

System.out.println("name"+i.getHostName);//打印本機的主機名

/*

如果IP地址和對應的主機名映射關係沒有在網絡上,主機出去找到地址,但沒解析成功

*/

 

 

InetAddress i = InetAddress.getByName("www.baidu.com");

System.out.println("address:"+i.getHostAddress());//打印本機的地址

System.out.println("name"+i.getHostName);//打印本機的主機名

 

}

B、端口號:

        a、用於標識進程的邏輯地址,不用進程的標識。

        b、有效端口:0 ~65535,系統使用或保留的端口是:0~ 1024。

每個網絡程序都會至少有一個邏輯端口

 

 

C、傳輸協議:

        即通信規則,包含TCP協議和UDP協議

UDP協議

        是面向無連接,明確了對方的端口,無論在不在網上,只管傳輸,不在就會丟失數據。只求速度,應用於網絡視頻會議和聊天等應用程序中。


協議特點:

         a、面向無連接,即將數據及源和目的封裝成數據包中,不建立鏈接的發送

         b、每個數據包的大小限制在64K之內

         c、因無連接,是不可靠的協議

         d、不建立連接,速度快。

TCP

        是面向連接的,必須連接成功才能傳輸數據,應用於下載等程序上

 

協議特點:

         a、面向連接,在建立連接後,形成傳輸數據的通道

         b、在連接中進行大數據量的傳輸

         c、通過三次握手完成連接,是可靠的協議

         d、必須建立連接,效率稍慢

三次握手:第一次本方發送請求,第二次對方確認連接,第三次本方再次確認連接成功。

 

通信的步驟:

        1)找到IP地址

        2)數據要發送到對象指定應用程序,爲標識這些應用程序,所以給這些網絡應用程序都用數字標識,爲方便稱呼這個數字,叫做端口,即邏輯端口。

        3)定義通信規則,稱之爲協議。國際組織定義了通用協議,即TCP/IP。

注意:必須要有數字標識才能將數據發送到應用程序上。

Socket(插座/套接字)

網絡上具有唯一標識的IP地址和端口號組合在一起才能構成唯一能識別的標識符套接字。

Socket就是爲網絡服務提供的一種機制

通信的兩端都有Socket

網絡通信其實就是Socket間的通信

數據在兩個Socket間通過io傳輸 

Socket原理機制:

通信的兩端都有Socket

網絡通信其實就是Socket間的通信。

數據在兩個Socket間通過IO傳輸。

 

UDP傳輸

DatagramSocketDatagramPacket

建立發送端,接收端。

建立數據包。

調用Socket的發送接收方法。

關閉Socket

發送端與接收端是兩個獨立的運行程序。

基本思路:

        發送端

              a、建立UDPSocket服務,在此無需指定端口,也可以將端口加入。如果不指定的話,系統會隨機分配一個端口,如第一次運行時端口爲1093,那麼第二次就會順延爲1094,再運行會一直順延,因爲之前的端口還沒有得到釋放,所以會順延端口號值。

              b、提供數據,並將數據封裝到數據包中

              c、通過socket服務的發送功能,將數據包發送出去

              d、關閉資源

        客戶端

              a、定義UDPSocket服務。通常會監聽一個端口,其實就是給這個接收網路應用程序定義數字標識,方便於明確哪些數據過來該應用程序可以處理。

              b、定義一個數據包,用來存儲接收到的字節數據,因爲數據包對象中有更多功能可以提取字節數據中的不同數據信息。

              c、通過socket服務的receive方法接收到的數據存入已定義好的數據包中

              d、通過數據包對象的特有功能,將這些不同的數據取出,打印在控制檯上

              e、關閉資源

        在定義接收數據的方法中,仍會在DatagramSocket構造函數中傳入DatagramPacket的參數,這是因爲收到的數據太多,需要解析,通過將數據封裝成對象,易於解析,所以需要傳入參數。

注意:

        1、發送端與接收端是兩個獨立的運行程序。

        2、在發送端,要在數據包對象中明確目的地IP及端口。

        3、在接收端,要指定監聽的端口。

演示用例: 

UDP通訊:

/*

 *  UDP協議接收數據步驟:

 *  A:創建接收端Socket服務對象

 *  B:創建數據包(接收容器

 *  C:調用接收方法

 *  D:解析數據包,把數據顯示在控制檯

 *  E:釋放資源

端口一定要一致。*/

public class ReceiveDemo {

public static void main(String[] args) throws IOException {

// 創建接收端Socket服務對象

// DatagramSocket(int port)

DatagramSocket ds = new DatagramSocket(12306);

 

// 創建數據包(接收容器)

// public DatagramPacket(byte[] buf,int length)

byte[] bys = new byte[1024];

DatagramPacket dp = new DatagramPacket(bys, bys.length);

 

// 調用接收方法

// public void receive(DatagramPacket p)

ds.receive(dp);

 

// 解析數據包,把數據顯示在控制檯

// public InetAddress getAddress()

InetAddress address = dp.getAddress();

String ip = address.getHostAddress();

 

// public byte[] getData()

byte[] bys2 = dp.getData();

// public int getLength()

int length = dp.getLength();

 

String s = new String(bys2, 0, length);

System.out.println(ip + "***" + s);

 

// 釋放資源

ds.close();

}

}

/*

 * UDP協議發送數據步驟:

 * A:創建發送端Socket服務對象

 * B:創建數據,並把數據打包 

 * C:發送數據

 * D:釋放資源

 */

public class SendDemo {

public static void main(String[] args) throws IOException {

// 創建發送端Socket服務對象

DatagramSocket ds = new DatagramSocket();

 

// 創建數據,並把數據打包

String str = "hello,udp,我來了";

// public DatagramPacket(byte[] buf,int length,InetAddress address,int

// port)

byte[] bys = str.getBytes();

int length = bys.length;

InetAddress address = InetAddress.getByName("192.168.3.172");

int port = 12306;

DatagramPacket dp = new DatagramPacket(bys, length, address, port);

 

// 發送數據

// public void send(DatagramPacket p)

ds.send(dp);

 

// 釋放資源

ds.close();

}

}

測試用例:

用鍵盤錄入的方式,來發送數據

 

class Send{

public static void main(String[] args)throws Exception{

//創建UDP服務。通過DatagramSocket對象

DatagramSocket ds = new DatagramSocket();//可以指定應用程序發送

//確定數據,並封裝成數據包

BufferedReader bufr  = new BufferedReader(new InputStreamReader(System.in));

String line = null;

while ((line = bufr.readLine())!=null){

if ("886".equals(line)){

break;

byte[] data = line.getBytes();

}

}

DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.254"),10000);

//通過Socket服務,將已有的數據包發送出去,通過send方法

ds.send(dp);

}

//關閉資源

ds.close();

}

}

class Receve{

public static void main(String[] args)throws Exception{

//創建udp Socket,建立端點

DatagramSocket ds = new DatagramSocket(10000);

while (true){

//定義數據包,用於存儲數據

byte[] buf = new byte[1024];

DatagramPacket dp = new DatagramPacket(buf,buf,length);

//通過服務的receive方法將收到的數據存入數據包中

ds.receive(dp);//阻塞式方法,沒有數據等待

//通過數據包的方法獲取其中的數據

String ip = dp.getAddress().getHostAddress();

String data = new String(dp.getData(),0,dp.getLength());

//int port = dp.getPort();

System.out.println(ip+""+data);

//關閉資源

}

}

//ds.close();

}

}

/*

 

編寫一個聊天程序。

有收數據的部分和發數據的部分,收發數據同時進行

使用多線程,一個線程控制收,一個線程控制發。

 

 

因爲收和發動作是不一致的,所以要定義兩個run方法,

 

*/

import java.util.*;

import java.io.*;

class Send implements Runnable{

private DatagramSocket ds

public  Send(DatagramSocket ds){

this.ds = ds;

}

public void run(){

try{

BufferedReader bufr  = new BufferedReader(new InputStreamReader(System.in));

String line = null;

while ((line = bufr.readLine())!=null){

if ("886".equals(line)){

break;

byte[] data = line.getBytes();

DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10000);

ds.send(dp)

}

}

}

catch (Exception e){

throw new RuntimeException("發送失敗");

}

}

}

class Rece implements Runnable{

private DatagramSocket ds

public Send(DatagramSocket ds){

this.ds = ds;

}

public void run(){

try{

while (true){

byte[] buf = new byte[1024];

DatagramPacket dp = new DatagramPacket(buf,buf,length);

ds.receive(dp);//阻塞式方法,沒有數據等待

 

String ip = dp.getAddress().getHostAddress();

String data = new String(dp.getData(),0,dp.getLength());

 

System.out.println(ip+""+data);

}

catch (Exception e){

throw new RuntimeException("結束失敗");

}

}

}

class ChatDemo{

public static void main(String[] args)throws Exception{

DatagramSocket sendSocket = new DatagramSocket();

DatagramSocket receSocket = new DatagramSocket(10000);

new Thread(new send(sendSocket)).start();

new Thread(new send(receSocket)).start();

}

}

TCP傳輸

SocketServerSocket

建立客戶端和服務器端

建立連接後,通過Socket中的IO流進行數據的傳輸

關閉socket

同樣,客戶端與服務器端是兩個獨立的應用程序。

1、TCP分客戶端和服務端。

客戶端對應的對象是Socket

服務端對應的對象是ServerSocket

2、方法:

        1)創建客戶端對象:

              Socket():創建空參數的客戶端對象,一般用於服務端接收數據

              Socket(String host,int port),指定要接收的IP地址和端口號

        2)創建服務端對象:ServerSocket(int port):指定接收的客戶端的端口

        3)Socket accept():監聽並接受到此套接字的連接

        4)void shutdownInput():此套接字的輸入流至於流的末尾

        5)void shutdownOutput():禁用此套接字的輸出流

        6)InputStream getInputStream():返回此套接字的輸入流,Socket對象調用

        7)OutputStream getOutputStream():返回套接字的輸出流,Socket對象調用

3、基本思路

客戶端:

1:建立客戶端的Socket服務,並明確要連接的服務器。

2:如果連接建立成功,就表明,已經建立了數據傳輸的通道.就可以在該通道通過IO進行數據的讀取和寫入.該通道稱爲Socket,Socket流中既有讀取流,也有寫入流.

3:通過Socket對象的方法,可以獲取這兩個流

4:通過流的對象可以對數據進行傳輸

5:如果傳輸數據完畢,關閉資源

服務端:

1:建立服務器端的socket服務,需要一個端口

2:服務端沒有直接流的操作,而是通過accept方法獲取客戶端對象,在通過獲取到的客戶端對象的流和客戶端進行通信

3:通過客戶端的獲取流對象的方法,讀取數據或者寫入數據

4:如果服務完成,需要關閉客戶端,然後關閉服務器,但是,一般會關閉客戶端,不會關閉服務器,因爲服務端是一直提供服務的

 

import java.util.*;

import java.io.*;

class TcpClient{

public static void main(String[] args)throws Exception{

//創建客戶端的Socket服務。指定目的主機和端口

Socket s = new Socket("192.168.1.254",10003);

//爲了發送數據,應該獲取Socket流中的輸出流

OutputStream out = s.getOutStream();

out.write("".getBytes());

s.close();

}

}

/*

需求:定義端點接收數據並打印在控制檯上

 

 

服務端:

1、建立服務端的Socket服務。ServerSocket();

並監聽一個端口

2、獲取連接過來的客戶端對象

通過ServerSocketaccept方法。沒有連接就會等待,所以這個方法是阻塞式的

3、客戶端如果發過來數據,那麼服務端要使用對應的客戶端對象,並獲取到該客戶端對象的讀取流讀取發過來的數據

並打印在控制檯。

4、關閉服務端(可選)

*/

class TcpSever{

public static void main(String[] args){

//建立服務端Socket服務。並監聽一個端口

ServerSocket ss = new ServerSocket(10003);

//通過accept方法獲取連接過來的客戶端對象

Socket s = ss.accept();

String ip = s.getInetAddress().getHostAddress();

System.out.println(ip+"....connected");

//獲取客戶端發送過來的數據,那麼要使用客戶端對象的讀取流來讀取數據

InputStream in = s.getInputStream();

byte[] buf = new byte[1024];

System.out.println(new String(buf,0,len));

s.close();//關閉客戶端

 

 

}

}

測試用例:

/*

 

需求:客戶端給服務端發送數據,服務端收到後給客戶端反饋信息

*/

 

/*

客戶端:

1、建立Socket服務。指定要連接主機和端口

2、獲取Socket流中的輸出流。將數據寫到該流中。通過網絡發送給服務端

3、獲取Socket流中的輸入流,將服務端反饋的數據獲取到,並打印

4、關閉客戶端資源。

*/

class {

public static void main(String[] args)throws Exception{

Socket s = new Socket("192.168.1.254",10004);

OutputStream out = s.getOutputStream();

out.write("".getBytes());

InputStream in = s.getInputStream();

byte[] buf = new byte[1024];

int len = in.read(buf);

System.out.println(new String(buf,0,len));

s.close();

}

}

class TcpServer2{

public static void main(String[] args)throws Exception{

ServerSocket ss = new ServerSocket(10004);

Socket s = ss.accept();

 

String ip = s.getInetAddress().getHostAddress();

 

System.out.println(ip+".....connected");

InputStream in= s.getInputStream();

byte[] buf = new byte[1024];

int len = in.read(buf);

System.out.println(new String(buf,0,len));

OutputStream out = s.getOutputStream();

out.write("".getBytes());

s.close();

ss.close();

 

}

}

複製文件。要特別注意的是結束標記的定義。

import java.io.*;

import java.net.*;

class TextClient{

public static void main(String[] args)throws Exception{

Socket s  = new Socket("",10006);//try異常,連接失敗直接出來,不在繼續執行

 

BufferedReader bufr = new

BufferedReader(new FileReader("IPDemo.java"));//讀取異常

 

PrintWriter out = new PrintWriter (s.getOutputStream(),true);

//DataOutputStream  dos = new DataOutputStream(s.getOutputStream());

//long time = System.currentTimeMillis();

//dos.wrieLone(time);//第二種方式

 

String line = null;

while ((line=bufr.readLine())!=null){

out.println(line);

}

 

out.println("over");

BufferedReader bufIn = new

BufferedReader(new InputStreamReader(s.InputStream());

String str = bufIn.readLine();

System.out.println(str);

bufr.close(;

s.close();

}

}

 

 

class TextSever{

public static void main(String[] args)throws Exception{

ServerSocket ss = new ServerSocket(10006);

Socket s  = ss.accept();

 

String ip = s.getInetAddress().getHostAddress();

System.out.println(ip+"....connected");

//DataInputStream dis = new DataInputStream(s.getInputStream());

//long l = dis.readLine

 

BufferedReader bufIn = new

BufferedReader(new InputStreamReader(s.InputStream());

 

PrintWriter out = new PrintWriter(new FileWriter("server.txt"),true);

String line = null;

 

while ((line=bufr.readLine())!=null){

//if ("over".equals(line))//第一種技術方式,缺點在於不安全,文件本身就具有over

//break;

out.println(line);

}

s.shutdownOutput();//關閉客戶端的輸出流,相當於給流中加入一個結束標記-1

PrintWriter  pw = new PrintWriter(s.getOutputStream(),true);

pw.println("上傳成功");

 

out.close();

s.close();

ss.close();

}

}

/*

需求:建立一個文本轉換服務器

客戶端給服務端發送文本,服務端會將文本轉成大寫再返回給客戶端

而且客戶端可以不斷的進行文本轉換,當客戶端輸入over時,轉換就結束

 

 

分析:

客戶端:

既然是操作設備上的數據,那麼就可以使用io技術,並按照io的操作規律來思考。

源:

鍵盤錄入

目的:

網絡輸出流

而且操作的是文本數據。可以選擇字符流。

步驟:

1、建立Socket服務

2、獲取鍵盤錄入

3、將數據發送給服務端

4、獲取服務端返回的大寫數據

5、結束,關資源

都是文本數據,可以使用字符流進行操作,同時提高效率,加入緩衝

*/

import java.util.*;

import java.io.*;

class TransClient{

public static void main(String[] args)throws Exception{

Socket s = new Socket("",10005);

//定義讀取鍵盤數據的流對象

BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

//定義目的,將數據寫入到Socket輸出流,發給服務端

BufferedWriter bufOut = 

new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

//PrintWriter out = new PrintWriter(s.getOutputStream(),true);

//定義一個Socket讀取流,讀取服務端返回的大寫信息

BufferedReader bufIn = 

new BufferedReader(new InputStreamReader(s.getInputStream()));

String line = null;

 while ((line = bufr.readLine())!=null){

 bufOut.write(line);

 if ("over".equals(line)){

 break;

 //out.println(line);

bufOut.write(line);

bufOut.newLine();//

bufOut.flush();

String str = bufIn.readLine();

System.out.println("server"+str);

 }

bufr.close();

s.close();

 }

 

}

}

/*

服務端:

源:

Socket讀取流

目的:

Socket輸入流

都是文本

 

*/

 

class TransServer{

public static void main(String[] args)throws Exception{

ServerSocket ss = new ServerSocket(10005);

 

Socket s = ss.accept();

String ip = s.getInetAddress().getHostAddress();

System.out.println(ip+".....connected");

 //讀取Socket讀取流中的數 

BufferedReader bufIn = 

new BufferedReader(new InputStreamReader(s.getInputStream()));

//目的Socket輸出流。將大寫數據寫入到Socket輸出流,併發送給客戶端

BufferedWriter bufOut = 

new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

 //PrintWriter out = new PrintWriter (s.getOutputStream(),true);

 String line = null;

while ((line = bufr.readLine())!=null){//readLine對到回車符纔會返回數據,而是等待繼續讀

//out.println(line.toUpperCase());

bufOut.write(line.toUpperCase());

bufOut.newLine();

bufOut.flush();

}

s.close();

ss.close();

}

}

客戶端併發登錄

        客戶端通過鍵盤錄入用戶名,服務端對這個用戶名進行校驗。

        如果該用戶存在,在服務端顯示xxx,已登陸;並在客戶端顯示xxx,歡迎光臨。

        如果用戶不存在,在服務端顯示xxx,嘗試登陸;並在客戶端顯示xxx,該用戶不存在。

        最多就登錄三次。

代碼: 

import java.io.*;

import java.net.*;

class {

public static void main(String[] args)throws Exception{

Socket s = new Socket("",10008);

 

BufferedReader bufr = 

new  BufferedReader(new InputStreamReader(System.in));

 

PrintWriter out = new PrintWriter(s.getOutputStream(),true);

 

BufferedReader bufIn = 

new BufferedReader(new InputStreamReader(s.getInputStream());

for (int x = 0;x<3 ;x++ ){

String line = bufr.readLine();//1返回可能爲空

if (line == null){

break;

}

out.println(line);//1將空發回服務端

 

String info = bufIn.readLine();//2已經登錄,

 

System.out.println("info:"+info);

if (info.contains("歡迎")){

break;

}

}

bufr.close();

s.close();

}

}

class UserThread implements Runnable{

private Socket s;

PicThread(Socket s){

this.s = s;

}

public void run(){

String ip = s.getInetAddress().getHostAddress();

System.out.println(ip+".....connected");

 

try{

for (int x =0;x<3 ;x++ ){

BufferedReader bufIn = 

new BufferedReader(new InputStreamReader(s.getInputStream());

String name = bufIn.readLine();

if (name == null){

break;

}

BufferedReader bufIn = 

new BufferedReader(new FileReader("user.txt"));

PrintWriter out = new PrintWriter(s.gerOutputStream(),true);

String line =null;

boolean flag = false;

while ((line = bufr.readLine())!=null){

if (line.equals(name){

flag = true;

break;

}

}

if (flag){

System.out.println(name+",已登錄");

out.println(name+"歡迎光臨");

break;

}else{

System.out.println(name+",嘗試登錄");

out.println(name+"用戶不存在");

}

}

s.close();

}

catch (Exception e){

throw new RuntimeException(ip+"校驗失敗");

}

}

 

class LoginServer{

public static void main(String[] args){

ServerSocket ss = new ServerSocket(10008);

while (true){

Socket s = ss.accept();

new Thread(new UserThread(s)).start();

}

}

}

TCP-客戶端併發上傳文件

要求:圖片格式爲jpg

  圖片大小不超過5M

import java.io.*;

import java.net.*;

class {

public static void main(String[] args)throws Exception{

if (args.length!=1){

System.out.println("請選擇一個jpg格式的圖片");

return;

}

File file = new File(args[0]);

if (!(file.exists()&&file.isFile())){

System.out.println("該文件有問題,要麼不存在,要麼不是文件");

return;

}

if (!file.getName().endsWith(".jpg")){

System.out.println("圖片格式錯誤,請重新選擇");

return;

}

if (file.length()>1024*5){

System.out.println("文件過大!");

return;

}

Socket s  = new Socket("",);

 

FileInputStream fis = new FileInputStream(file);

 

OutputStream out = s.getOutputStream();

 

byte[] buf = new byte[1024];

int len = 0;

 while ((len=fis.read(buf))!=-1){

 out.write(buf,0,len);

 }

 

 

 //告訴服務端數據以寫完

 s.shutdownOutput();

 InputStream in = s.getInputStream();

 byte[] bufIn = new byte[1024];

 int num = in.read(bufIn);

 System.out.println(new String(bufIn,0,num));

 fis.close();

 s.close();

}

}

 

/*

服務端

這個服務端有個侷限性。當A客戶端連接上以後。被服務端獲取到。服務端執行具體流程。

這時B客戶端連接,只有等待。

因爲服務端還沒有處理完A客戶端的請求,還有循環回來執行下次accept方法。所以

暫時獲取不到B客戶端對象。

 

那麼爲了可以讓多個客戶端同時併發訪問服務端,服務端最好就是將每個客戶端封裝到一個單獨的線程中

這樣,就可以同時處理多個客戶端請求

只要明確了每一個客戶端要在服務端執行的代碼即可。將該代碼存入run方法

*/

class PicThread implements Runnable{

private Socket s;

PicThread(Socket s){

this.s = s;

}

public void run(){

String ip = s.getInetAddress().getHostAddress();

try{

System.out.println(ip+".....connected");

InputStream in = s.getInputStream();

 

 

File file = new File(ip+"("+(count)+")"+".jpg");

while (file.exists()){

file = new File(ip+"("+(count++)+")"+".jpg");

}

 

 

FileOutputStream fos = new FileOutputStream("server.bmp");

byte[] buf = new byte[1024];

int len = 0;

 while ((len=fis.read(buf))!=-1){

 fos.write(buf,0,len);

 }

 OutputStream out = s.getOutputStream();

 out.write("上傳成功".getBytes());

 fos.close();

 s.close();

}

catch (Exception e){

throw new RuntimeException("上傳失敗!");

}

}

}

class {

public static void main(String[] args){

ServerSocket ss = new ServerSocket(10007);

while (true){

Socket s = ss.accept();

new Thread(new PicThread(s)).start();

}

}

}

 

客戶端和服務的瀏覽器演示

        瀏覽器是一個標準的客戶端,它可以對服務端傳送過來的數據消息進行解析,把符合應用層協議的消息部分解析後,將頭信息拆包掉,傳送到應用層,只保留了正確的正文主題部分顯示在主體部分上。

        而由於使用java編譯是在傳輸層和網際層處理的,所以,會接受到全部的消息,包含了頭消息。而瀏覽器處於應用層,已將發送來的頭消息去除,只留下了主體信息。

示例:

自定義服務器,用瀏覽器訪問:

import java.io.*;

import java.net.*;

//服務器 

class ServerDemo{

public static void main(String[] args){

  //創建服務,監聽端口  

ServerSocket ss = new ServerSocket(11000);

//獲取客戶端 

Socket s = ss.accept();

//顯示ip  

System.out.println(s.getInetAddress().getHostAddress());

 //讀取客戶端讀取流數據  

InputStream in = s.getInputStream();

byte[] buf = new byte[1024];

int len = in.read(buf);

 //顯示數據  

System.out.println(new String(buf,0,len));

//返回信息寫入客戶端輸出流 

PrintWriter out = new PrintWriter(s.getOutputStream(),true);

out.println("<font color='red' size='7'>客戶端你好</font>");

s.close();

ss.close();

}

 

URL和URLConnection

1、URL:

        URI:範圍更大,條形碼也包含於此範圍

        URL:範圍較小,即域名

方法:

        1)構造函數:URL(String protocol,String host,int port,String file);//根據指定 protocol、host、port號和 file 創建 URL對象。

        2)String getProtocol();//獲取協議名稱

        3)String getHost();//獲取主機名

        4)int getPort();//獲取端口號

        5)String getFile();//獲取URL文件名

        6)String getPath();//獲取此URL的路徑部分

        7)String getQuery();//獲取此URL的查詢部,客戶端傳輸的特定信息

注:一般輸入網址,是不帶端口號的,此時可進行獲取,通過獲取網址返回的port,若port爲-1,則分配一個默認的80端口,如

        int port = getPort();

        if(port == -1)

              port = 80;

2、URLConnection

方法:

        1)URLConnection openConnection();//用URL調用此方法,返回一個 URLConnection 對象,它表示到 URL 所引用的遠程對象的連接。

        2)InputStream getInputStream();//獲取輸入流

        3)OutputStream getOutputStream();//獲取輸出流

演示用例:

/* 

自定義瀏覽器,顯示網頁信息 

*/  

  

import java.io.*;  

import java.awt.*;  

import java.awt.event.*;  

import java.net.*;  

  

class MyIEGUIDemo  

{  

    //定義所需組件引用  

    private Frame f;  

    private Button but,bok;  

    private TextField tf;  

    private TextArea ta;  

  

    //構造函數  

    MyIEGUIDemo()  

    {  

        init();  

    }  

  

    //窗體基本設置於功能實現  

    public void init()  

    {  

        //組件實例化  

        f=new Frame("我的Window");  

        but=new Button("跳轉");  

        tf=new TextField(50);  

        ta=new TextArea(25,60);  

  

        //基本設置  

        f.setBounds(300,150,500,500);  

        f.setLayout(new FlowLayout());  

  

        //添加組件  

        f.add(tf);  

        f.add(but);  

        f.add(ta);  

  

        //窗體事件  

        myEvent();  

  

        //窗體顯示  

        f.setVisible(true);  

    }  

  

    //註冊事件  

    public void myEvent()  

    {  

        //窗體關閉功能  

        f.addWindowListener(new WindowAdapter()  

        {  

            public void windowClosing(WindowEvent e)  

            {  

                System.exit(0);  

            }  

        });  

  

        //“跳轉按鈕事件  

        but.addActionListener(new ActionListener()  

        {  

            public void actionPerformed(ActionEvent e)  

            {  

                showFile();//顯示網頁內容在文本區中  

            }  

        });  

  

          

  

        //文本框鍵盤事件  

       tf.addKeyListener(new KeyAdapter()  

        {  

            public void keyPressed(KeyEvent e)  

            {  

                //如果鍵盤按下Enter鍵,就將網頁內容顯示在文本區中  

                if(e.getKeyCode()==KeyEvent.VK_ENTER)  

                    showFile();  

            }  

        });  

    }  

  

    //顯示網頁內容  

        private void showFile()  

        {  

            ta.setText("");  

            String path=tf.getText();//獲取輸入的路徑  

            try  

            {  

                //封裝地址對象  

URL url =new URL(path);  

連接網頁服務器  

                URLConnection conn=url.openConnection();  

                //讀取流,用於讀取服務器返回數據  

                InputStream in=conn.getInputStream();  

  

                byte[] buf=new byte[1024*1024];  

  

                int len=in.read(buf);  

                //將數據顯示在文本區中  

                ta.append(new String(buf,0,len));  

            }  

            catch (Exception e)  

            {  

                throw new RuntimeException("連接"+path+"網站失敗");  

            }  

        }  

  

    public static void main(String[] args)   

    {  

        //運行窗體  

        new MyIEGUIDemo();  

    }  

   

小知識點

1、Socket類的構造函數中,有一個空參數的構造函數:

        Socket()//通過系統默認類型的 SocketImpl創建未連接套接字對象

       可以通過connect(SocketAddress endpoint)方法來連接服務器。而SocketAddress是一個抽象類,它的子類InetSocketAddress實現了IP套接字地址(IP地址+端口號)。所以就可以連接到服務器了。

2、ServerSocket對象中的構造函數:

        ServerSocket(int port,int backlog),其中的backlog表示隊列的最大長度,即最多連入客戶端的個數,即最大連接數。

3、在瀏覽器輸入網址訪問一臺主機所做的操作:

       如輸入http://61.135.169.125,可以直接連接此ip的主機,而我們一般是輸入主機名:http:/www.baidu.ocm(百度主機對應的ip地址就是:61.135.169.125),那麼此時瀏覽器做了神馬操作呢?

       也就是說如何通過主機名獲取IP地址,從而連接到這臺主機呢?這就需要將主機名翻譯成IP地址,即域名解析:DNS

        在進行訪問的時候,會先在本地的hosts文件(c:\windows\system32\drivers\ext\host)中找對應的映射。若有,則直接返回請求;若無,則到公網的映射列表即DNS中找對應的映射,找到後,將主機名對應的IP地址返回給本機,本機通過這個IP地址找到對應的服務器。

示意圖:


 host應用:可屏蔽一些惡意網址,即將對應的映射關係寫入hosts中,將IP地址改爲本機的迴環地址,那麼會直接找到hosts,就不會將請求發送出去了。

 

-----------android培訓java培訓、java學習型技術博客、期待與您交流!------------ 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章