-------android培訓、java培訓、java學習型技術博客、期待與您交流! ----------
知識點一 網絡編程概述
一、概述:
1、網絡模型:OSI參考模型和TCP/IP參考模型
2、網絡通信要素:IP地址,端口號、傳輸協議。
1)IP地址:
A、它是網絡中的設備標識
B、不易記憶,可用主機名錶示,兩者存在映射關係
C、本機迴環地址:127.0.0.1,主機名爲:localhost
2)端口號:
A、用於標識進程的邏輯地址,不用進程的標識。
B、有效端口:0 ~ 65535,系統使用或保留的端口是:0 ~ 1024.
3)傳輸協議:
A、即通信規則,包含TCP和UDP協議
B、UDP是面向無連接,無論在不在網上,只管傳輸,不在就會丟失數據。只求速度,應用於網絡視頻會議和聊天等應用程序中
協議特點:
a、面向無連接,即將數據及源和目的封裝成數據包中,不建立鏈接的發送
b、每個數據報的大小限制在64K之內
c、因無連接,是不可靠的協議
d、不建立連接,速度快。
C、TCP是面向連接的,必須連接成功才能傳輸數據,應用於下載等程序上
協議特點:
a、面向連接,在建立連接後,形成傳輸數據的通道
b、在連接中進行大數據量的傳輸
c、通過三次握手完成連接,是可靠的協議
d、必須建立連接,效率稍慢
三次握手:第一次本方發送請求,第二次對方確認連接,第三次本方再次確認連接成功。
3、通信的步驟:
1)找到IP地址
2)數據要發送到對象指定應用程序,爲標識這些應用程序,所以給這些網絡應用程序都用數字標識,爲方便稱呼這個數字,叫做端口,即邏輯端口。
3)定義通信規則,稱之爲協議。國際組織定義了通用協議,即TCP/IP。
注意:必須要有數字標識才能將數據發送到應用程序上。
示意圖:
二、網絡模型:
1、對於TCP/IP協議,開發處於傳輸層和網際層
應用層:FTP和HTTP協議等
傳輸層:UDP和TCP等
網際層:IP
三、網絡通信要素:
一)IP地址:java中對應的是InetAddress類,存在於java.net包中。
InetAddress類:
1、無構造函數,可通過getLocalHost()方法獲取InetAddress對象,此方法是靜態的,返回此對象。
InetAddress i = InetAddress.getLocalHost();
2、方法:
1)static InetAddress getByName(String host):在給定主機名的情況下獲取主機的IP地址
2)static InetAddress[] getAllByName(String host):在給定主機名的情況下,根據系統上配置的名稱服務返回IP地址所組成的數組。返回對象不唯一時,用此方法。
3)String getHostAddress():返回IP地址字符串文本形式,以這個爲主,即以IP地址爲主。
4)String getHostName():返回IP地址主機名。
3、如何獲取任意一臺主機的IP地址對象:
1)功能:返回InetAddress對象
2)對於任意主機,需要指定傳入主機名的參數
注意:如果IP地址和主機名無映射關係,或者沒有在網絡上,就不會解析成功
示例:
import java.net.*;
class IPDemo
{
public static void main(String[] args) throws Exception
{
/*
//獲取本機IP地址
InetAddress i = InetAddress.getLocalHost();
System.out.println(i.toString());
System.out.println("address:"+i.getHostAddress());
System.out.println("name:"+i.getHostName());
*/
//通過百度主機名獲取其IP地址
InetAddress ia = InetAddress.getByName("www.baidu.com");
System.out.println("address:"+ia.getHostAddress());
System.out.println("name:"+ia.getHostName());
}
}
知識點二 Socket編程
一、概述:
1、Socket:
1)它被稱之爲插座,相當於港口一樣,是網絡服務提供的一種機制。
2)通信兩端都要有Socket,才能建立服務。
3)網絡通信其實就是Socket間的通信,數據在兩個Socket間通過IO傳輸。
二、UDP傳輸:
1、通過類DatagramSocket,此類表示用發送和接收數據報的套接字,即Socket
2、方法:
1)創建 UDPSocket發送服務對象:DatagramSocket(),可不指定端口
2)創建 UDPSocket接收服務對象:DatagramSocket(int port)
3)發送:void send(DatagramPacket p)
4)接收:void receive(DatagramPacket p)
其中DatagramPacket:數據報包用來實現無連接包投遞服務的,每條報文僅根據該包中包含的信息從一臺機器路由到另一臺機器中。
凡是帶地址(InetAddress)的都是用於發送包的。
3、流程:
1)發送數據:
思路:
a.建立UDPSocket服務,在此無需指定端口,也可以將端口加入。如果不指定的話,系統會隨機分配一個端口,如第一次運行時端口爲1093,那麼第二次就會順延爲1094,再運行會一直順延,因爲之前的短路還沒有得到釋放,所以會順延端口號值。
b.提供數據,並將數據封裝到數據包中
c.通過socket服務的發送功能,將數據包發送出去
d.關閉資源
2)接收數據:
思路:
a.定義UDPSocket服務。通常會監聽一個端口,其實就是給這個接收網路應用程序定義數字標識方便於明確那些數據過來該應用程序可以處理。
b.定義一個數據包,因爲要存儲接收到的字節數據,因爲數據包對象中有更多功能可以提取字節數據中的不同數據信息。
c.通過socket服務的receive方法接收到的數據存入已定義好的數據包中
d.通過數據包對象的特有功能,將這些不同的數據取出,打印在控制檯上
e.關閉資源
注意:
在定義接收數據的方法中,仍會在DatagramSocket構造函數中傳入DatagramPacket的參數,這是因爲收到的數據太多,需要解析,通過將數據封裝成對象,易於解析,所以需要傳入參數。
示例:
1.
import java.net.*;
/*
需求:通過udp傳輸方式,將一段文字數據發送出去。,
定義一個udp發送端。
思路:
1,建立updsocket服務。
2,提供數據,並將數據封裝到數據包中。
3,通過socket服務的發送功能,將數據包發出去。
4,關閉資源。
UdpDemo.java裏沒有UdpDemo這個主類?爲什麼???
*/
class UdpSend
{
public static void main(String[] args) throws Exception
{
//1,創建udp服務。通過DatagramSocket對象。
DatagramSocket ds = new DatagramSocket(8888);
//2,確定數據,並封裝成數據包。DatagramPacket(byte[] buf, int length, InetAddress address, int port)
//getBytes()方法可以字符串裝換成字節數組
byte[] buf = "udp ge men lai le ".getBytes();
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10000);
//3,通過socket服務,將已有的數據包發送出去。通過send方法。
ds.send(dp);
//4,關閉資源。
ds.close();
}
}
/*
需求:
定義一個應用程序,用於接收udp協議傳輸的數據並處理的。
定義udp的接收端。
思路:
1,定義udpsocket服務。通常會監聽一個端口。其實就是給這個接收網絡應用程序定義數字標識。
方便於明確哪些數據過來該應用程序可以處理。
2,定義一個數據包,因爲要存儲接收到的字節數據。
因爲數據包對象中有更多功能可以提取字節數據中的不同數據信息。
3,通過socket服務的receive方法將收到的數據存入已定義好的數據包中。
4,通過數據包對象的特有功能。將這些不同的數據取出。打印在控制檯上。
5,關閉資源。
dos下輸入start可以開啓兩個dos命令窗口
*/
class UdpRece
{
public static void main(String[] args) throws Exception
{
//1,創建udp socket,建立端點。
DatagramSocket ds = new DatagramSocket(10000);
while(true)
{
//2,定義數據包。用於存儲數據。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//3,通過服務的receive方法將收到數據存入數據包中。
ds.receive(dp);//阻塞式方法。
//4,通過數據包的方法獲取其中的數據。
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println(ip+"::"+data+"::"+port);
}
//5,關閉資源
//ds.close();
}
}
2.UDP-鍵盤錄入方式傳遞數據
示例:
/*
import java.net.*;
import java.io.*;一般同時使用
192.168.0.1代表網絡段和192.168.0.255代表這個網絡段中的主機地址
*/
import java.net.*;
import java.io.*;
class UdpSend2
{
public static void main(String[] args) throws Exception
{
//1,創建udp socket。
DatagramSocket ds = new DatagramSocket();
//讀取鍵盤錄入
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
String line = null;
//readLine()用read的阻塞式方法。TCP的時候要用到
while((line=bufr.readLine())!=null)
{
if("886".equals(line))
break;
//2,確定數據,並封裝成數據包。DatagramPacket(byte[] buf, int length, InetAddress address, int port)
//getBytes()方法可以字符串裝換成字節數組
byte[] buf = line.getBytes();
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10001);
//3,通過socket服務,將已有的數據包發送出去。通過send方法。
ds.send(dp);
}
//4,關閉資源。
ds.close();
}
}
class UdpRece2
{
public static void main(String[] args) throws Exception
{
//1,創建udp socket,建立端點。
DatagramSocket ds = new DatagramSocket(10001);
while(true)
{
//2,定義數據包。用於存儲數據。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//3,通過服務的receive方法將收到數據存入數據包中。
ds.receive(dp);//阻塞式方法。
//4,通過數據包的方法獲取其中的數據。
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+"::"+data);
}
}
}
練習:編寫一個簡單的聊天程序:
分析:
有收數據的部分,有發數據的部分,這兩部分需要同時執行,那就需要多線程技術,一個線程控制接收,一個線程控制發。
因爲收和發的動作不一致,所以要定義兩個run方法,而且這個兩個方法要封裝到不同的類中。
import java.io.*;
import java.net.*;
//發送數據
class SendSocket implements Runnable
{
//定義全局變量
private DatagramSocket ds;
//初始化發送類對象的參數
public SendSocket(DatagramSocket ds)
{
this.ds = ds;
}
//覆寫run方法,此線程發送鍵盤錄入的數據
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[] b = line.getBytes();
//創建發送服務對象,將數據發送出去
DatagramPacket dp =
new DatagramPacket(b,b.length,InetAddress.getByName("192.168.1.255"),10001);
ds.send(dp);
}
//關閉資源
ds.close();
}
catch (Exception e)
{
throw new RuntimeException("發送失敗");
}
}
}
//接收數據
class ReceSocket implements Runnable
{
//定義全局變量
private DatagramSocket ds;
//初始化接收類對象的參數
public ReceSocket(DatagramSocket ds)
{
this.ds = ds;
}
//覆寫run方法,此線程接收數據
public void run()
{
try
{
//循環讀取接收到的數據
while(true)
{
//創建字節數組存儲數據
byte[] by = new byte[1024];
//創建接收數據的對象,接收數據
DatagramPacket dp = new DatagramPacket(by,by.length);
ds.receive(dp);
//獲取發送方的ip地址
String ip = dp.getAddress().getHostAddress();
//getData獲取byte數組中的數據
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(10001);
//創建兩個線程,同時執行
new Thread(new SendSocket(sendSocket)).start();
new Thread(new ReceSocket(receSocket)).start();
}
}
三、TCP傳輸
1、包括客戶端和服務端,即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():返回此套接字的輸入流
7)OutputStream getOutputStream():返回套接字的輸出流
3、流程:
1)建立客戶端和服務器端。
2)建立連接後,通過Socket中的IO流進行數據的傳輸。
3)關閉socket
同樣的,客戶端與服務端是兩個獨立的應用程序。
4、客戶端:在該對象建立時就可去連接指定主機,因爲TCP是面向倆接的,所以在建立Socket服務時,就要有服務端存在,並連接成功,形成通路後,再通過該通道進行數據的傳輸。
步驟:
1)創建Socket服務,並指定要連接的主機端口。通路一建立,就會產生Socket流(包括輸入流和輸出流),通過方法獲取
2)爲了發送數據,應獲取Socket中的輸出流,如果要接收服務端的反饋信息,需要獲取Socket的輸入流
3)通過write()方法將信息寫入到流中。
4)關閉Socket流資源
5、服務端:定義端連接數據,並存放指定地方,需監聽一個端口。
步驟:
1)建立服務端的Socket服務,通過ServerSocet帶端口參數的構造函數
2)獲取連接過來的客戶對象,通過ServerSocket的accept()方法,此方法是阻塞式的,如果服務端沒有連接到就會等待。
3)客戶端若發來數據,則服務端要使用對應的客戶端對象,並獲取到該客戶端對象的讀取流發來的數據,並輸出到指定目的地。
4)關閉服務端。一般服務端是常開的,因爲在實際應用中,隨時有客戶端在請求連接和服務,所有這裏需要定時關閉客戶端對象流,避免某一個客戶端長時間佔用服務器端。
示例1:
/*
演示tcp傳輸。
1,tcp分客戶端和服務端。
2,客戶端對應的對象是Socket。
服務端對應的對象是ServerSocket。
*/
/*
客戶端,
通過查閱socket對象,發現在該對象建立時,就可以去連接指定主機。
因爲tcp是面向連接的。所以在建立socket服務時,
就要有服務端存在,並連接成功。形成通路後,在該通道進行數據的傳輸。
需求:給服務端發送給一個文本數據。
*/
import java.io.*;
import java.net.*;
class TcpClient
{
public static void main(String[] args) throws Exception
{
//創建客戶端的socket服務。指定目的主機和端口
Socket s = new Socket("127.0.0.1",10003);
//爲了發送數據,應該獲取socket流中的輸出流。
OutputStream out = s.getOutputStream();
out.write("tcp ge men lai le ".getBytes());
s.close();
}
}
/*
需求:定義端點接收數據並打印在控制檯上。
服務端:
1,建立服務端的socket服務。ServerSocket();
並監聽一個端口。
2,獲取連接過來的客戶端對象。
通過ServerSokcet的 accept方法。沒有連接就會等,所以這個方法阻塞式的。
3,客戶端如果發過來數據,那麼服務端要使用對應的客戶端對象,並獲取到該客戶端對象的讀取流來讀取發過來的數據。
並打印在控制檯。
4,關閉服務端.(可選)
*/
class TcpServer
{
public static void main(String[] args) throws Exception
{
//建立服務端socket服務。並監聽一個端口。
ServerSocket ss = new ServerSocket(10003);
//通過accept方法獲取連接過來的客戶端對象。
while(true)
{
Socket s = ss.accept();
//從客戶端對象獲取ip地址
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));
s.close();//關閉客戶端.
}
//把服務端也關閉了
//ss.close();
}
}
示例2:
import java.io.*;
import java.net.*;
/*
演示tcp的傳輸的客戶端和服務端的互訪。
需求:客戶端給服務端發送數據,服務端收到後,給客戶端反饋信息。
*/
/*
客戶端:
1,建立socket服務。指定要連接主機和端口。
2,獲取socket流中的輸出流。將數據寫到該流中。通過網絡發送給服務端。
3,獲取socket流中的輸入流,將服務端反饋的數據獲取到,並打印。
4,關閉客戶端資源。
*/
class TcpClient2
{
public static void main(String[] args)throws Exception
{
Socket s = new Socket("127.0.0.1",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();
//得到客戶端的ip地址
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();
Thread.sleep(10000);
out.write("哥們收到,你也好".getBytes());
s.close();
//把服務端也關閉了
ss.close();
}
}
6、TCP練習:
建立一個文本轉換服務器。
客戶端給服務端發送文本,服務單會將文本轉成大寫在返回給客戶端。而且客戶度可以不斷的進行文本轉換。當客戶端輸入over時,轉換結束。
分析:既然是設備上的數據,則可用IO技術,並依IO操作規律操作。
1、客戶端:
源:鍵盤錄入;目的:網絡設備,即網絡輸出流。-->操作的是文本數據,可選字符流,並加入高效緩衝區。
步驟:
1)建立服務。
2)獲取鍵盤錄入
3)將數據發給服務端
4)之後去服務端返回數據
5)結束,關閉資源
2、服務端:
源:socket讀取流;目的:socket輸出流。
注:socket的close()方法中加入了結束標記-1,所以客戶端結束了,服務端也結束了。
3、該例可能出現的問題:
現象:客戶端和服務端都在莫名的等待,似乎數據都沒有傳輸過去。
原因:因爲客戶端和服務端都有阻塞方法,這些方法沒有讀到結束標記,就會一直等待,而導致兩端都在等待。
解決:需要用到刷新和換行的方式將寫入和讀取的數據從流中刷新到內存中
方式一:可用高效緩衝區類的newLine()換行作爲結束標記,並用flush()進行刷新。
方式二:可用PrintWriter(s.getOutputStrean(),true)創建輸出流對象,true作用是刷新,通過打印方法println()換行,“ln”表示換行。
import java.io.*;
import java.net.*;
//客戶端
class TransClient
{
public static void main(String[] args) throws Exception
{
Socket s = new Socket("127.0.0.1",10005);
//定義讀取鍵盤數據的流對象。
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//定義目的,將數據寫入到socket輸出流。發給服務端。
//BufferedWriter是字符流,getOutputStream()是字節流,所以要通過OutputStreamWriter轉化
//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)
{
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=bufIn.readLine())!=null)
{
System.out.println(line);
//toUpperCase()將字符轉換成大寫
out.println(line.toUpperCase());
// bufOut.write(line.toUpperCase());
// bufOut.newLine();
// 刷新緩衝區
// bufOut.flush();
}
//s.close()就是在Socket流裏面加入一個-1,結束標記
s.close();
ss.close();
}
}
7、TCP複製文件
1、客戶端:
源:硬盤上的文件;目的:網絡設備,即網絡輸出流。-->若操作的是文本數據,可選字符流,並加入高效緩衝區。若是媒體文件,用字節流。
2、服務端:
源:socket讀取流;目的:socket輸出流。
3、出現的問題:現象:
a.文件已經上傳成功了,但是沒有得到服務端的反饋信息。
b.即使得到反饋信息,但得到的是null,而不是“上傳成功”的信息
原因:
a.因爲客戶端將數據發送完畢後,服務端仍然在等待這讀取數據,並沒有收到結束標記,就會一直等待讀取。
b.上個問題解決後,收到的不是指定信息而是null,是因爲服務端寫入數據後,也需要刷新,才能將信息反饋給客服端。
解決:
a.方法一:定義結束標記,先將結束標記發送給服務端,讓服務端接收到結束標記,然後再發送上傳的數據。但是這樣定義可能會發生定義的標記和文件中的數據重複而導致提前結束。
方法二:定義時間戳,由於時間是唯一的,在發送數據前,先獲取時間,發送完後在結尾處寫上相同的時間戳,在服務端,接收數據前先接收一個時間戳,然後在循環中判斷時間戳以結束標記。
方法三:通過socket方法中的shutdownOutput(),關閉輸入流資源,從而結束傳輸流,以給定結束標記。這裏主要用這個方法。
import java.io.*;
import java.net.*;
class TextClient
{
public static void main(String[] args) throws Exception
{
Socket s = new Socket("127.0.0.1",10006);
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();
// out.println(time);
// dos.writeLong(time);
String line = null;
while((line=bufr.readLine())!=null)
{
out.println(line);
}
// dos.writeLong(time);
s.shutdownOutput();//關閉客戶端的輸出流。相當於給流中加入一個結束標記-1.
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
String str = bufIn.readLine();
System.out.println(str);
bufr.close();
s.close();
}
}
class TextServer
{
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.InputStream());
// long l=dis.readLong();
// BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter out = new PrintWriter(new FileWriter("server.txt"),true);
String line = null;
while((line=bufIn.readLine())!=null)
{
//if("over".equals(line))
//break;
out.println(line);
}
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println("上傳成功");
out.close();
s.close();
ss.close();
}
}
最新最全的的java學習視頻教程:http://pro.net.itcast.cn/View-22-1458.aspx
-------android培訓、java培訓、java學習型技術博客、期待與您交流! ----------
詳細請查看:http://edu.csdn.net/heima