——- android培訓、java培訓、期待與您交流! ———-
網絡編程
UDP和TCP協議各自優缺點
1)UDP面向無連接,數據報大小有限制64K之內封包,不可靠,容易丟包,但是速度快!因此針對於數據不重要,追求速度快的常用此類協議,如聊天工具/網絡視頻會議。
2)TCP面向連接,可傳輸大樹據,經過三次握手協議,可靠,但是速度慢,消耗資源。TCP相當於打電話,UDP相當於郵局寄信。下載用的是TCP。
(一)UDP協議
- UDP發送數據步驟四步
1)創建socket對象
2)確定數據,並把數據打包
3)發送數據
4)關閉資源socket
public class UDPSend
{
public static void main(String args[])
{
DatagramSocket ds = new DatagramSocket(8888);
byte[] buf = "hello world!".getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length,"192.163.1.34","10000");
ds.send(dp);
ds.close();
}
}
- UDP接受數據步驟
1)創建socket對象,並指定接受端口號
2)定義空的數據報,準備存儲接受到的數據,並利用數據報中的方法解析數據的各種信息。
3)接收數據
4)解析數據
5)關閉資源
public class UDPRece
{
public static void main(String args[])
{
DatagramSocket ds = new DatagramSocket(8888);
byte[] buf = new byte[1024];//最大爲6k
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp);
String address = dp.getAdrress().getHostAdrress();
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println(address+"..."+data+"..."+port);
ds.close();
}
}
192.168.1.255是個廣播地址。可以給所有機器發廣播。
示例1: 從鍵盤接收數據,可以一對一發,也可以羣發。
public class UDPSend
{
public static void main(String args[])
{
DatagramSocket ds = new DatagramSocket(8888);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line=br.readLine())!=null)
{
if(line.equals("bye"))
break;
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.163.1.34"),"10000");
ds.send(dp);
}
ds.close();
}
}
public class UDPRece
{
public static void main(String args[])
{
DatagramSocket ds = new DatagramSocket(8888);
while(true)
{
byte[] buf = new byte[1024];//最大爲6k
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp);
String address = dp.getAdrress().getHostAdrress();
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println(address+"..."+data+"..."+port);
}
}
}
示例2:聊天工具,用兩個線程來寫,同樣是從鍵盤錄入數據。
class UDPSend implements Runnable
{
private DatagramSocket ds;
public UDPSend(DatagramSocket ds)
{
this.ds = ds;
}
public void run()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line=br.readLine())!=null)
{
if(line.equals("bye"))
break;
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.163.1.34"),"10000");
ds.send(dp);
}
ds.close();
}
}
class UDPRece implements Runnable
{
private DatagramSocket ds;
public UDPRece(DatagramSocket ds)
{
this.ds = ds;
}
public void run()
{
while(true)
{
byte[] buf = new byte[1024];//最大爲6k
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp);
String address = dp.getAdrress().getHostAdrress();
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();
System.out.println(address+"..."+data+"..."+port);
}
}
}
public class TestDemo
{
public static void main(String args[])
{
DatagramSocket ds1 = new DatagramSocket();
DatagramSocket ds2 = new DatagramSocket(10000);
new Thread(new UDPSend(ds1));
new Thread(new UDPRece(ds2));
}
}
(二)TCP協議
注1:客戶端和服務器
注2:需要建立連接才能執行,需要先運行服務器,在運行客戶端
客戶端建立步驟
1. 創建socket對象,並設置連接地址和端口號
2. 獲得輸出流
3. 加載數據到輸出流中
4. 關閉客戶端
class TCPClient
{
public static void main(String args[])
{
Socket sc = new Socket("192.168.1.254",10003);
OutputStream out = ss.getOutputStream();
out.write("hello world".getBytes());
sc.close();
}
}
服務器建立步驟
1. 創建server對象,設置自己的監聽端口號
2. 獲得客戶端socket對象
3. 獲得客戶端對象的輸入流,並讀取數據
4. 關閉客戶端。
class TCPServer
{
public static void main(String args[])
{
ServerSocket ss = new ServerSocket(10003);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+".......");
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
s.close();
}
}
示例3:客戶端發送數據,服務器端接收數據,並反饋給客戶端,客戶端接收反饋並打印。
class TCPClient
{
public static void main(String args[])
{
Socket s = new Socket("192.168.1.254",10001);
OutputStream out = s.getOutputStream();
out.write("你好,服務器!".getBytes());
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len=in.read(buf))!=-1)
{
System.out.println(new String(buf,0,len));
}
s.close();
}
}
class TCPServer
{
public static void main(String args[])
{
ServerSocket ss = new ServerSocket(10001);
Socket s = ss.accept();
String ip = 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));
OutputStream out = s.getOutputStream();
out.write("客戶端,謝謝,已收到!!".getBytes());
s.close();
ss.close();
}
}
示例4:客戶端通過鍵盤錄入文本數據,服務器將文本轉換成大寫返回給客戶端。
1)用字符緩衝流完成
2)改錯。客戶端和服務器都在莫名的等待,原因是裏面出現了阻塞式方法,這些方法沒有讀到結束標記,所以一直處於等待狀態。
class TCPClient
{
public static void main(String args[])
{
Socket s = new Socket("192.168.1.254",10002);
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufbw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
BufferedReader bufbr = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line=in.readLine())!=null)
{
if(line.equals("over"))
break;
bufbw.write(line);
bufbw.newLine();
bufbw.flush();
String str = bufr.readLine();
System.out.println("server:"+str);
}
}
}
class TCPServer
{
public static void main(String args[])
{
ServerSocket ss = new ServerSocket(10003);
Socket s = ss.accept();
BufferedReader bufbr = new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter bufbw = new BufferedReader(new OutputStreamWriter(s.getOutputStream()));
String line = null;
while((line = bufbr.readLine())!=null)
{
bufbw.write(line.toUpperCase());
bufbw.newLine();
bufbw.flush();
}
s.close();
ss.close();
}
}
示例5:上傳文本數據。從客戶傳送硬盤文件到服務器端硬盤。文本文件。
出現問題:
1)沒有結束標記位
2)socket內部定義的結束關閉流標誌位的方法。shutdownOutput();
class TCPClient
{
public static void main(String args[])
{
Socket s = new Socket("192.168.1.254",10002);
BufferedReader in = new BufferedReader(new FileReader("c:\\client.txt"));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(s.getOutputStream()),true);
bufbr = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line=in.readLine())!=null)
{
pw.println(line);
}
s.shutdownOutput();
String str = bufr.readLine();
System.out.println("server:"+str);
in.close();
s.close();
}
}
class TCPServer
{
public static void main(String args[])
{
ServerSocket ss = new ServerSocket(10003);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
BufferedReader bufbr = new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\server.txt"),true);
String line = null;
while((line = bufbr.readLine())!=null)
{
bw.write(line);
}
PrintWriter bufbw = new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
bufbw.write("上傳成功!!!!")
s.close();
ss.close();
}
}
示例6:上傳圖片。(練習)
一個傳一個收。
class TCPClient
{
public static void main(String args[])
{
Socket s = new Socket("192.168.1.254",10002);
InputStream in = new FileInputStream("c:\\1.png");
OutputStream out = s.getOutputStream();
byte[] buf = new byte[1024];
int len = 0 ;
while((len=in.read(buf))!=-1)
{
out.write(buf);
}
s.shutdownOutput();
InputStream is = s.getInputStream();
byte[] buf1 = new byte[1024];
int len1 = is.read(buf1);
System.out.println(new String(buf1,0,len1));
in.close();
s.close();
}
}
class TCPServer
{
public static void main(String args[])
{
ServerSocket ss = new ServerSocket(10003);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
InputStream in = s.getInputStream();
OutputStream ou = new FileOutputStream("d:\\2.png");
byte[] buf = new byte[1024];
int len = 0;
while((len=in.read(buf))!=-1)
ou.write(buf,0,len);
OutputStream out = s.getOutputStream();
out.write("上傳成功!!".getBytes());
ou.close();
s.close();
ss.close();
}
}
一個服務器,同時可以處理多個客戶端的請求。用多線程處理。
注意:需要開啓新的線程,需要保存路徑是原有文件不被覆蓋,需要傳輸特定格式和特定大小的文件。
class uploadThread
{
private Socket socket;
public uploadThread(Socket socket)
{
this.socket = socket;
}
public void run()
{
try
{
int count = 1;
String ip = s.getInetAddress().getHostAddress();
InputStream in = s.getInputStream();
File file = new File(ip+"("+count+")"+".png");
while(file.exist())
{
File file = new File(ip+"("+(count++)+")"+".png");
}
OutputStream ou = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while((len=in.read(buf))!=-1)
ou.write(buf,0,len);
OutputStream out = s.getOutputStream();
out.write("上傳成功!!".getBytes());
ou.close();
s.close();
}catch(Exception e)
{
throw new RuntimeException(ip+"上傳失敗!!")
}
}
}
class TCPServer
{
public static void main(String args[])
{
ServerSocket ss = new ServerSocket(10003);
while(true)
{
Socket s = ss.accept();
new Thread(new uploadThread(s)).start();
}
}
}
class TCPClient
{
public static void main(String args[])
{
if(args.length!=1)
{
System.out.println("請選擇一個圖片格式的文件上傳");
return;
}
File file = args[0];
if(!(file.exist()&&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 s = new Socket("192.168.1.254",10002);
InputStream in = new FileInputStream(file);
OutputStream out = s.getOutputStream();
byte[] buf = new byte[1024];
int len = 0 ;
while((len=in.read(buf))!=-1)
{
out.write(buf);
}
s.shutdownOutput();
InputStream is = s.getInputStream();
byte[] buf1 = new byte[1024];
int len1 = is.read(buf1);
System.out.println(new String(buf1,0,len1));
in.close();
s.close();
}
}
(三) URL
我們所常用的瀏覽器,實際上就是一個客戶端,輸入內容爲http://IP:端口號?key=alue,就相當於發送請求。
我們把http協議,IP地址,端口號和請求內容封裝到一個類中,稱爲URL。
通過URL,我們可以:
(1)獲得協議名稱,IP地址,端口號,請求內容等;
(2)可以直接獲得客戶端和服務器端的連接通道。
public class URLDemo
{
public static void main(String args[]) throws Exception
{
URL url = new URL("http://192.168.1.245:8080/myweb/demo.html?name=hahha&age=39");
System.out.println("getProtocol()"+url.getProtocol());//獲得協議
System.out.println("getHost()"+url.getHost());//獲得IP地址
System.out.println("getPort()"+url.getPort());//獲得端口號
System.out.println("getPath()"+url.getPath());//獲得路徑
System.out.println("getFile()"+url.getFile());//獲得查找內容
System.out.println("getQuery()"+url.getQuery());//獲得查找內容
URLConnection con = url.getConnection();//返回數據沒有頭信息。//這屬於應用層。
InputStream in = url.openStream();//直接開流
}
}
需要注意一點:getPort()方法,如果沒有輸入端口號,則默認爲-1;需要如下判斷:
int port = url.getPort();
if(port==-1)
port = 80;
以上是網絡編程的基本概念和基本思想,多做練習,多理解,多複習。