Java 網路程式設計相關知識
網路的一些基礎知識
IP地址分類
IP地址根據網路ID的不同分為5種類型,A類地址、B類地址、C類地址、D類地址和E類地址。A類保留給政府機構,B類分配給中等規模的公司,C類分配給任何需要的人,D類用於組播,E類用於實驗,各類可容納的地址數目不同。(IP地址由網路號和主機號組成)。
-
A類地址
一個A類IP地址由1位元組的網路地址和3位元組主機地址組成,網路地址的最高位必須是“0”, 地址範圍從1.0.0.0 到126.0.0.0。可用的A類網路有126個,每個網路能容納1億多個主機。A類地址一般分配給大型網路。
- A類地址第1位元組為網路地址,其它3個位元組為主機地址,預設的子網掩碼255.0.0.0。
- A類地址範圍:0.0.0.0—126.255.255.255(0.0.0.0和126.255.255.255這種IP一般不使用)。
- A類地址中的私有地址和保留地址。
- 10.X.X.X是私有地址(所謂的私有地址就是在網際網路上不使用,而被用在區域網絡中的地址)。
- 127.X.X.X是保留地址,用做迴圈測試用的。
-
B類地址
一個B類IP地址由2個位元組的網路地址和2個位元組的主機地址組成,網路地址的最高位必須是“10”,地址範圍從128.0.0.0到191.255.255.255。可用的B類網路有16382個,每個網路能容納6萬多個主機,一般分配給中型網路 。
- B類地址第1位元組和第2位元組為網路地址,其它2個位元組為主機地址,預設子網掩碼是255.255.0.0。
- B類地址範圍:128.0.0.0—191.255.255.255(128.0.0.0和191.255.255.255這種地址一般不用)。
- B類地址的私有地址和保留地址。
- 172.16.0.0—172.31.255.255是私有地址。
- 169.254.X.X是保留地址。如果你的IP地址是自動獲取IP地址,而你在網路上又沒有找到可用的DHCP伺服器。就會得到其中一個IP。
-
C類地址
一個C類IP地址由3位元組的網路地址和1位元組的主機地址組成,網路地址的最高位必須是“110”。範圍從192.0.0.0到223.255.255.255。C類網路可達209萬餘個,每個網路能容納254個主機。
- C類地址前3個位元組為網路地址,第4個個位元組為主機地址。另外第1個位元組的前三位固定為110,預設子網掩碼是255.255.255.0。
- C類地址範圍:192.0.0.0—223.255.255.255(192.0.0.0和223.255.255.255一般不用)。
- C類地址中的私有地址。
- 192.168.X.X是私有地址。
-
D類地址
D類IP地址第一個位元組以“1110”開始,它是一個專門保留的地址。它並不指向特定的網路,目前這一類地址被用在多點廣播(Multicast)中。多點廣播地址用來一次定址一組計算機,它標識共享同一協議的一組計算機。
- D類地址不分網路地址和主機地址,它的第1個位元組的前四位固定為1110。
- D類地址範圍:224.0.0.1—239.255.255.254。
-
E類地址
以“11110”開始,為將來使用保留。全零(“0.0.0.0”)地址對應於當前主機。全“1”的IP地址(“255.255.255.255”)是當前子網的廣播地址。
- E類地址也不分網路地址和主機地址,它的第1個位元組的前五位固定為11110。
- E類地址範圍:240.0.0.1—255.255.255.254
埠分類
- 公認埠(well know port):從0到1023,他們緊密繫結一些特定服務。比如80埠繫結http服務,443埠繫結https服務,22埠繫結ssh服務等;
- 主粗埠(Registed port):1024到49151,他們鬆散的繫結一些服務,我們自己寫的應用程式應該使用這個範圍內的埠;
- 動態或私有埠:49152到65535,這些埠應用程式會動態使用。
Java的基本網路支援
Java中對網路支援的類大都在java.net這個包下面,常用的類有URL、URLConnection、URLDecoder、URLENcoder和InetAddress等。
InetAddress的使用
InetAddress代表IP地址,它有兩個子類Inet4Address和Inet6Address。
public class InetAddressDemo {
public static void main(String[] args) throws Exception {
//獲取本機的IP地址
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("host address:"+localHost.getHostAddress());
System.out.println("host name:"+localHost.getHostName());
//根據百度的域名,隨機獲取百度的一個IP地址
InetAddress baidu = InetAddress.getByName("www.baidu.com");
System.out.println("host address:"+baidu.getHostAddress());
System.out.println("host name:"+baidu.getHostName());
//根據IP地址,貨期InetAddress
InetAddress loopAddress = InetAddress.getByAddress(new byte[]{127,0,0,1});
System.out.println("host address:"+loopAddress.getHostAddress());
System.out.println("host name:"+loopAddress.getHostName());
//根據域名,獲取域名對應的所有IP地址
InetAddress[] baidus = InetAddress.getAllByName("www.baidu.com");
for (InetAddress inetAddress : baidus) {
System.out.println("host address:"+inetAddress.getHostAddress());
System.out.println("host name:"+inetAddress.getHostName());
}
}
}
URLEncoder和URLDecoder的使用
當URL地址中包含非西歐字元的字串時,系統會將這些非西歐字元自動編碼。在我們程式設計過程中就會涉及到將這些普通的字串和特殊字串之間的轉換,這時就需要使用URLEncoder和URLDecoder。
String encodedUrl = "https://www.baidu.com/s?wd=ip%E5%9C%B0%E5%9D%80%E5%88%86%E7%B1%BB&rsv_spt=1&rsv_iqid=0xe6273c5300071242&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&rqlang=cn&tn=baiduhome_pg&rsv_enter=1&oq=IP%25E5%259C%25B0%25E5%259D%2580%25E5%2588%2586%25E7%25B1%25BB&rsv_t=8798Pr4JiDoBuHX8kSW6i384TlOk5p8vEQ4c4tWrc0suF31CjvBh6stq0gyq0PtETa9x&inputT=9569&rsv_sug3=24&rsv_sug1=14&rsv_sug7=100&rsv_pq=b3c08c4200035639&bs=IP%E5%9C%B0%E5%9D%80%E5%88%86%E7%B1%BB";
String decodeURL = URLDecoder.decode(encodedUrl, Charset.forName("UTF8").name());
System.out.println("decodeURL:"+decodeURL);
URLEncoder和URLDecoder這兩個類只提供了encode和decode方法供我們使用。
URL、URLConnection和URLPermission的使用
這邊先講下URL和URI的區別:
URI:是uniform resource identifier,統一資源識別符號,用來唯一的標識一個資源。Web上可用的每種資源如HTML文件、影象、視訊片段、程式等都是一個來URI來定位的URI一般由三部組成:①訪問資源的命名機制②存放資源的主機名③資源自身的名稱,由路徑表示,著重強調於資源。
URL是uniform resource locator,統一資源定位器,它是一種具體的URI,即URL可以用來標識一個資源,而且還指明瞭如何locate這個資源。URL一般由三部組成:①協議(或稱為服務方式)②存有該資源的主機IP地址(有時也包括埠號)③主機資源的具體地址。
簡單的說,URI是一種網際網路上資源的唯一識別符號(我們可以將它看成一個資源的id),URL是一種特殊的URI,URL不僅能標識一個網際網路資源,而且通過URL能夠獲取到這個資源。Java中的兩個類URI和URL就分別對應這兩個概念。URL類可以開啟一個流來獲取具體的資源。
String[] urls = {"https://img0.pconline.com.cn/pconline/1707/21/9625301_20150814_6d20f056ee9803d9419buyemaASeB0KJ_thumb.jpg"};
for (String url : urls) {
URL url1 = new URL(url);
int port = url1.getPort();
System.out.println("port:"+port);
String host = url1.getHost();
System.out.println("host:"+host);
String protocol = url1.getProtocol();
System.out.println("protocol:"+protocol);
String file = url1.getFile();
System.out.println("fileName:"+file);
//這段是否要設定許可權,為什麼open總是失敗?
InputStream inputStream = url1.openStream();
FileOutputStream fos = new FileOutputStream("D:\\"+new Date()+".jpeg");
FileCopyUtils.copy(inputStream,fos);
inputStream.close();
fos.close();
}
基於TCP協議的網路程式設計
使用ServerSocket建立TCP服務端
Java中能夠接收其他通訊實體請求的類是ServerSocket。這個物件可以監聽來自客戶端的Socket連線(每個TCP連線兩個Socket,一個IP加一個埠組成一個Socket)。如果沒有連線,它將一直處於等待狀態。
- Socket accept():該方法返回客戶端Socket,沒有連線將一直處於等待狀態(同步),執行緒也被阻塞(阻塞);
ServerSocket存在如下的建構函式:
- public ServerSocket(int port):指定埠,backlog預設50;
- public ServerSocket(int port, int backlog) :backlog用於指定連線佇列的長度,這個值和作業系統也有關,嘗試了下Windows下最多設定200個,如果我們設定的值超過200,就取200。
- public ServerSocket(int port, int backlog, InetAddress bindAddr):如果機器有多個網絡卡還可以指定具體監聽哪個網絡卡。
(netstat 命令詳解)
使用Socket進行通訊
下面是一個很加單的clientSocket和serverSocket的列子:客戶端每隔一秒鐘給服務端發一個訊息,服務端給出響應:
Socket clientSocket = new Socket("127.0.0.1",30000);
//inputStream用來接受服務端返回的訊息
InputStream inputStream = clientSocket.getInputStream();
//outputStream用來給服務端發訊息
OutputStream outputStream = clientSocket.getOutputStream();
while (true){
outputStream.write(("hi, l am clinetSocket").getBytes());
byte[] bytes = new byte[1024];
inputStream.read(bytes);
System.out.println("get message from server:"+new String(bytes));
Thread.sleep(1000);
}
服務端程式
public class ServerSocketDemo {
private static ExecutorService executorService = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(30000, 5);
while (true) {
Socket socket = serverSocket.accept();
System.out.println("get socket:" + socket);
executorService.execute(new Printer(socket));
}
}
private static class Printer implements Runnable {
private Socket socket;
public Printer(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
OutputStream outputStream;
InputStream inputStream;
try {
//對於服務端來說,inputStream用來接收客戶端的報文
inputStream = socket.getInputStream();
//對於服務端來說,outputStream用來給客戶端響應報文
outputStream = socket.getOutputStream();
while (true) {
byte[] bytes = new byte[1024];
inputStream.read(bytes);
System.out.println("wa.. l got you " + new String(bytes));
outputStream.write("l am serverSocket".getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
半關閉的Socket
半關閉的Socket是指只關閉Socket的輸入輸出流,但是不關閉整個Socket連線。
使用NIO實現非阻塞的Socket通訊
Java NIO下幾個用於Socket通訊的類的簡單說明:
- Selector:它是SelectableChannel物件的多路複用器器,所有希望採用非阻塞方式進行通訊的Channel都應該註冊到Selector物件上。可以呼叫selector = Selector.open()來構造Selector物件。
- SelectionKey:一個Selector例項有三種SelectionKey集合。第一種是通過selector的keys()方法返回的所有SelectionKey集合,代表所有註冊在這個Selector例項上的Channel;第二種是通過selectedKeys()方法返回的SelectionKey集合,代表需要進行IO處理的channel;第三種是已經取消註冊的Channel,一般不用。
public class NioServerSocketDemo {
private Selector selector;
public static final int port = 30000;
private Charset charset = Charset.forName("UTF-8");
public void init() throws Exception{
selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress("127.0.0.1",port);
server.bind(address,5);
server.configureBlocking(false);
//serverSocketChannel也要註冊到selector上面
server.register(selector, SelectionKey.OP_ACCEPT);
//selector.select()會阻塞當前執行緒
//selector.select(long timeout),設定超時時間
//selector.selectNow()不會阻塞執行緒
while (selector.select()>0){
for(SelectionKey key : selector.selectedKeys()){
//已經處理過了,將其刪除
selector.selectedKeys().remove(key);
if(key.isConnectable()){
SocketChannel channel = (SocketChannel)key.channel();
System.out.println(channel+" has connected...");
}
if(key.isAcceptable()){
SocketChannel acceptChannel = server.accept();
acceptChannel.configureBlocking(false);
acceptChannel.register(selector,SelectionKey.OP_READ);
key.interestOps(SelectionKey.OP_ACCEPT);
}
if(key.isReadable()){
SocketChannel channel = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
String content = "";
try{
while (channel.read(buffer)>0){
buffer.flip();
content+=charset.decode(buffer);
}
System.out.println("get content:"+content);
channel.write(buffer);
key.interestOps(SelectionKey.OP_READ);
}catch (IOException ex){
key.cancel();
if(key.channel()!=null){
key.channel().close();
}
}
}
}
}
}
public static void main(String[] args) throws Exception {
new NioServerSocketDemo().init();
}
}
使用AIO實現非阻塞的Socket通訊
public class AIOServerSocket {
private static Charset charset = Charset.forName("UTF-8");
public static void main(String[] args) throws Exception {
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(30000),1000);
while (true){
Future<AsynchronousSocketChannel> future = serverSocketChannel.accept();
AsynchronousSocketChannel socketChannel = future.get();
ByteBuffer buffer = ByteBuffer.allocate(1024);
socketChannel.read(buffer);
System.out.println("get from client:"+charset.decode(buffer));
}
}
}