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培訓、期待與您交流! ----------