傳智播客Java自學筆記第23天(待編輯)

   java_網絡編程.

網絡通訊要素:

 IP地址:InetAddress

 網絡中設備的標識

 不易記憶,可用主機名

 本地迴環地址:127.0.0.1 主機名:localhost

端口號

 用於標識進程的邏輯地址,不同進程的標識

 有效端口:0~65535,其中0~1024系統使用或保留端口。
 
 傳輸協議

 通訊的規則


常見協議:TCP,UDP

TCP,UDP
UDP的特點:

 1.面向無連接
 2.數據會被封包.在64K內
 3.不可靠,因爲無連接.所以不可靠.
 4.速度快,因爲不需要連接

 例如.聊天的時候就是UDP的,還有視頻會議,桌面共享等.

TCP的特點:
 1.建立連接,形成傳輸數據的通道.
 2.在連接中進行大數據量傳輸
 3.通過三次握手完成連接,是可靠的協議
 4.必須建立連接,但是效率稍低

 例如,打電話就是TCP的.
 UDP就相當於對講機,TCP相當於電話,
 下載就是TCP的因爲不能丟數據.聊天就是UDP的


//-----------------------------------------------------------------------------

InetAddress 類 ,java.net包中.
 InetAddress類用於描述IP的類
  沒有構造方法.
   使用靜態方法來訪問本類對象

  常用方法:
   static | InetAddress getLocalHost() : 返回本地主機。

   static InetAddress getByName(String host) : 在給定主機名的情況下確定主機的 IP 地址。

   String getHostAddress() : 返回 IP 地址字符串(以文本表現形式)。

   String getHostName() : 獲取此 IP 地址的主機名。

示例:
獲取百度IP


 

import java.net.*;
class  IPDemo
{
 public static void main(String[] args) throws UnknownHostException
 {
   InetAddress ia= InetAddress.getByName("www.baidu.com");

   System.out.println(ia.getHostAddress());
 }
}


//-----------------------------------------------------------------------------

Socket

 Socket就是爲網絡服務提供的一種機制。
 
 通信的兩端都有Socket。
 
 網絡通信其實就是Socket間的通信。

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


 DatagramSocket 類 java.net 包中

  構造方法: 
   DatagramSocket()
     構造數據報套接字並將其綁定到本地主機上任何可用的端口。
     其他參與API.
   
   常用方法:
     void send(DatagramPacket p)
      從此套接字發送數據報包

 DatagramPacket 類 java.net 包中
  構造方法:
   DatagramPacket(byte[] buf, int length)
     構造 DatagramPacket,用來接收長度爲 length 的數據包。

   DatagramPacket(byte[] buf, int length, InetAddress address, int port)
     構造數據報包,用來將長度爲 length 的包發送到指定主機上的指定端口號。
     其他參與API.
  常用方法:
    int getPort()
    返回某臺遠程主機的端口號,此數據報將要發往該主機或者是從該主機接收到的。
    
    InetAddress getAddress()
     返回某臺機器的 IP 地址,此數據報將要發往該機器或者是從該機器接收到的。
    
    int getLength()
     返回將要發送或接收到的數據的長度。

//-----------------------------------------------------------------------------
需求:通過udp傳輸方式,將一段文字數據發送出去。,
定義一個udp發送端。
思路:
1,建立updsocket服務。
2,提供數據,並將數據封裝到數據包中。
3,通過socket服務的發送功能,將數據包發出去。
4,關閉資源。


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

2,定義一個數據包,因爲要存儲接收到的字節數據。
因爲數據包對象中有更多功能可以提取字節數據中的不同數據信息。
3,通過socket服務的receive方法將收到的數據存入已定義好的數據包中。
4,通過數據包對象的特有功能。將這些不同的數據取出。打印在控制檯上。
5,關閉資源。

示例:數據傳輸

import java.net.*;
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) 

  byte[] buf = "udp ge men lai le ".getBytes();
  DatagramPacket dp = 
   new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.254"),10000);

  //3,通過socket服務,將已有的數據包發送出去。通過send方法。
  ds.send(dp);

  //4,關閉資源。

  ds.close();
 }
}


 


示例:接收數據

import java.net.*;
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();

 }
}



//-----------------------------------------------------------------------------
示例:

/*
編寫一個聊天程序。
有收數據的部分,和發數據的部分。
這兩部分需要同時執行。
那就需要用到多線程技術。
一個線程控制收,一個線程控制發。

因爲收和發動作是不一致的,所以要定義兩個run方法。
而且這兩個方法要封裝到不同的類中。

*/
import java.net.*;
import java.io.*;
class Send implements Runnable//發送端
{
 private DatagramSocket ds;
 public Send(DatagramSocket ds)
 {
  this.ds=ds;
 }
 public void run()//多線程覆蓋run方法
 {
  try
  {
   //鍵盤錄入
   BufferedReader bufr=
    new BufferedReader(new InputStreamReader(System.in));

   String len=null;

   while((len=bufr.readLine())!=null)
   {
    //轉換成字節數組發送數據
    byte[] buf=len.getBytes();

    DatagramPacket dp=
     new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10001);

    ds.send(dp);

    if("886".equals(len))
     break;
   }
  }
  catch (Exception e)
  {
   throw new RuntimeException("發送端失敗");
  }
 }
}
class Rece implements Runnable//接收端
{
 private DatagramSocket ds;
 public Rece(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);

    //獲取IP
    String ip=dp.getAddress().getHostAddress();

    //將包轉化成字符串
    String data=new String(dp.getData(),0,dp.getLength());

    if("886".equals(data))
    {
     System.out.println(ip+"...離開聊天室");
     break;
    }
    //打印
    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 Send(sendSocket)).start();
  new Thread(new Rece(receSocket)).start();
 }
}



//-----------------------------------------------------------------------------
TCP傳輸

Socket和ServerSocket

Tcp分客戶端和服務端。
客戶端對應的對象是Socket。
服務端對應的對象是ServerSocket。

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

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

 
Socket 類 java.net包中
 此類實現客戶端套接字(也可以就叫“套接字”)。套接字是兩臺機器間通信的端點。

 常用構造方法:
    Socket(String host, int port)
    創建一個流套接字並將其連接到指定主機上的指定端口號。
    其他參閱api.

 方法:
  OutputStream | getOutputStream()
    返回此套接字的輸出流。

  InputStream getInputStream()
    返回此套接字的輸入流。

  void | shutdownInput()
    此套接字的輸入流置於“流的末尾”。

  void | shutdownOutput() //關閉客戶端的輸出流。相當於給流中加入一個結束標記-1.
    禁用此套接字的輸出流。

 

ServerSocket 類 java.net 包中
 此類實現服務器套接字。服務器套接字等待請求通過網絡傳入。它基於該請求執行某些操作,然後可能向
 請求者返回結果。

 常用構造方法:
  ServerSocket(int port)
    創建綁定到特定端口的服務器套接字。

 方法:
   Socket accept()
    偵聽並接受到此套接字的連接。 將ServerSocket變成Socket

   InetAddress getInetAddress()
    返回此服務器套接字的本地地址。


客戶端
通過查閱socket對象,發現在該對象建立時,就可以去連接指定主機。
因爲tcp是面向連接的。所以在建立socket服務時,
就要有服務端存在,並連接成功。形成通路後,在該通道進行數據的傳輸。

需求:給服務端發送給一個文本數據。

步驟:

1,創建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,關閉服務端。(可選)

import java.io.*;
import java.net.*;
class  TcpServer//服務端
{
 public static void main(String[] args) throws Exception
 {
  //建立服務端socket服務。並監聽一個端口。
  ServerSocket ss = new ServerSocket(10003);

  //通過accept方法獲取連接過來的客戶端對象。
  while(true)
  {
   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));

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



//-----------------------------------------------------------------------------
示例:

/*

需求:建立一個文本轉換服務器。
客戶端給服務端發送文本,服務單會將文本轉成大寫在返回給客戶端。
而且客戶度可以不斷的進行文本轉換。當客戶端輸入over時,轉換結束。

分析:
客戶端:
既然是操作設備上的數據,那麼就可以使用io技術,並按照io的操作規律來思考。
源:鍵盤錄入。
目的:網絡設備,網絡輸出流。
而且操作的是文本數據。可以選擇字符流。

步驟
1,建立服務。
2,獲取鍵盤錄入。
3,將數據發給服務端。
4,後去服務端返回的大寫數據。
5,結束,關資源。

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


*/
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輸出流。發給服務端。
  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);

   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輸出流,併發送給客戶端。
  PrintWriter out = new PrintWriter(s.getOutputStream(),true);

  String line = null;

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

   System.out.println(line);

   out.println(line.toUpperCase());
  }
  s.close();

  ss.close();
 }
}



//-----------------------------------------------------------------------------
示例:上傳文本文件


 

import java.io.*;
import java.net.*;//導入包

class  TextClient
{
 public static void main(String[] args) throws Exception
 {
  //建立服務端socket服務。並監聽一個端口。
  Socket s = new Socket("127.0.0.1",10006);
  
  //和指定文件關聯
  BufferedReader bufr = 
   new BufferedReader(new FileReader("IPDemo.java"));
  
  //發出數據
  PrintWriter out = new PrintWriter(s.getOutputStream(),true);

  String line = null;
  while((line=bufr.readLine())!=null)
  {
   out.println(line);
  }

  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();

  //獲取並打印IP
  String ip = s.getInetAddress().getHostAddress();
  System.out.println(ip+"....connected");

  //接收客戶端的數據
  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();
 }
}


//-----------------------------------------------------------------------------

示例:上傳圖片。

/*
需求:上傳圖片。

 

*/
/*
客戶端。
1,服務端點。
2,讀取客戶端已有的圖片數據。
3,通過socket 輸出流將數據發給服務端。
4,讀取服務端反饋信息。
5,關閉。

*/

import java.io.*;
import java.net.*;
class  PicClient
{
 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*1024*5)
  {
   System.out.println("文件過大,沒安好心");
   return ;
  }
  
  //建立Socket服務,往服務端發送數據
  Socket s = new Socket("127.0.0.1",10007);

  //建立字節流和文件相關聯
  FileInputStream fis = new FileInputStream(file);

  //使用Socket輸出方法網服務端寫出數據
  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();

  //使用Socket輸出方法網服務端接收數據
  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;
 }
 //對現場的Run方法
 public void run()
 {
  //定義計數器,爲了不讓存入的文件相同
  int count = 1;

  //獲取客戶端連進來的IP
  String ip  = s.getInetAddress().getHostAddress();
  try
  {
   //打印客戶端IP
   System.out.println(ip+"....connected");

   //接收客戶端傳來的圖片
   InputStream in = s.getInputStream();

   //創建了路徑.並存入格式比如 127.0.0.1(1).jpg
   File dir =  new File("d:\pic");

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

   //如果文件存在的話就用while循環判斷文件是否存在.知道不存在添加
   while(file.exists())
    file = new File(dir,ip+"("+(count++)+")"+".jpg");

   //輸出
   FileOutputStream fos = new FileOutputStream(file);

   byte[] buf = new byte[1024];

   int len = 0;
   while((len=in.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(ip+"上傳失敗");
  }
 }
}

class  PicServer//服務端
{
 public static void main(String[] args) throws Exception
 {
  //接收客戶端傳來的數據
  ServerSocket ss = new ServerSocket(10007);

  while(true)
  {
   //阻塞是方法
   Socket s = ss.accept();
   
   //爲了實現多臺客戶端同時上傳圖片使用多線程
   new Thread(new PicThread(s)).start(); 
  }

  //ss.close();
 }
}



//-----------------------------------------------------------------------------
示例:

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

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

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

最多就登錄三次。
*/
import java.io.*;
import java.net.*;//導入包

class  LoginClient//客戶端
{
 public static void main(String[] args) throws Exception
 {
  //建立客戶端
  Socket s = new Socket("192.168.1.254",10008);

  //鍵盤錄入
  BufferedReader bufr = 
   new BufferedReader(new InputStreamReader(System.in));

  //定義目的,將數據寫入到socket輸出流。發給服務端。
  PrintWriter out = new PrintWriter(s.getOutputStream(),true);

  //接收服務端返回的數據
  BufferedReader bufIn =
   new BufferedReader(new InputStreamReader(s.getInputStream()));

  //for循環,因爲最多可以登錄3次.
  for(int x=0; x<3; x++)
  {
   //將鍵盤錄入的字符,轉換成字符串,並判斷是否是空.如果是直接退出,如果不是傳個服務端
   String line = bufr.readLine();
   if(line==null)
    break;
   out.println(line);

   //接收並打印服務端返回的數據.判斷是否返回的是"歡迎".
   String info = bufIn.readLine();
   System.out.println("info:"+info);
   if(info.contains("歡迎"))
    break;
  }
  //關閉流
  bufr.close();
  s.close();
 }
}


class UserThread implements Runnable
{
 //接收傳進來的客戶端
 private Socket s;
 UserThread(Socket s)
 {
  this.s = s;
 }
 public void run()
 {
  //獲取並打印客戶端的IP
  String ip = s.getInetAddress().getHostAddress();
  System.out.println(ip+"....connected");
  try
  {
   //最多就登錄三次。用for訓話
   for(int x=0; x<3; x++)
   {
    //接收客戶端傳來的數據
    BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));

    //判斷傳進來的數據是否爲空
    String name = bufIn.readLine();
    if(name==null)
     break;

    //建立字符輸入流和文件相關聯,
    BufferedReader bufr = new BufferedReader(new FileReader("user.txt"));

    //建立給客戶端的傳輸的數據
    PrintWriter out = new PrintWriter(s.getOutputStream(),true);

    String line = null;

    //定義一個布爾型變量,來判斷用戶是否存在
    boolean flag = false;

    //循環,並判斷客戶端傳進來的數據.和服務端的數據是否相同.
    //如果是退出循環,並顯示xxx.已登錄,和返回給客戶端"xxx歡迎光臨"
    //如果輸入不是在服務端顯示XXX嘗試登陸,並返回給客戶端xxxz用戶名不存在
    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) throws Exception
 {
  //服務端
  ServerSocket ss = new ServerSocket(10008);

  while(true)
  {
   //阻塞式方法
   Socket s = ss.accept();
   
   //建立多線程,因爲N多的服務端要訪問
   new Thread(new UserThread(s)).start();
  }
 }
}


 

------- android培訓java培訓、期待與您交流! ---------- 

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