一、TCP協議通信
- 1.上傳圖片練習
- 1)將圖片上傳到服務器端保存。
示例代碼:
package com.heisejiuhuche.socket;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.Socket;
public class PicUploadClient {
public static void main(String[] args) throws Exception {
upload();
}
private static void upload() throws Exception {
/* 創建Socket對象,指定主機和端口 */
Socket s = new Socket("localhost", 10007);
/* 創建File對象,關聯文件 */
File file = new File("C://Users//jeremy//Documents//javaTmp//me.jpg");
/* 創建輸入輸出流對象 */
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
int b = 0;
/* 上傳文件 */
while((b = bis.read()) != -1) {
bos.write(b);
bos.flush();
}
/* 發送結束符 */
s.shutdownOutput();
/* 獲取反饋信息 */
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int msg = in.read(buf);
System.out.println(new String(buf, 0, msg));
bis.close();
s.close();
}
}
package com.heisejiuhuche.socket;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class PicUploadServer {
public static void main(String[] args) throws Exception {
recv();
}
private static void recv() throws Exception {
ServerSocket ss = new ServerSocket(10007);
Socket s = ss.accept();
/* 創建輸入輸出流對象 */
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
File file = new File("C://Users//jeremy//Documents//me.jpg");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
int b = 0;
/* 保存文件 */
while((b = bis.read()) != -1) {
bos.write(b);
bos.flush();
}
/*發送反饋信息 */
OutputStream out = s.getOutputStream();
out.write("上傳成功".getBytes());
bos.close();
s.close();
ss.close();
}
}
程序運行結果:上傳成功
- 2)TCP客戶端併發上傳圖片
- 這個服務端有侷限性。當一個客戶端連接服務器之後,因爲服務器是單線程,那麼其他客戶端如果想連接服務器,只能帶前一個客戶完成所有任務,才能連接。要同時處理多個客戶端請求,應該明確客戶端要在服務器端要執行的代碼,然後將每個客戶端封裝到一個線程當中,同步執行。
示例代碼:
package com.heisejiuhuche.socket;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.Socket;
public class UploadPicByThreadClient {
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("localhost", 10007);
/* 創建File對象,關聯文件 */
// File file = new File("C://Users//jeremy//Documents//javaTmp//me.jpg");
/* 創建輸入輸出流對象 */
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
int b = 0;
/* 上傳文件 */
while((b = bis.read()) != -1) {
bos.write(b);
bos.flush();
}
/* 發送結束符 */
s.shutdownOutput();
/* 獲取反饋信息 */
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int msg = in.read(buf);
System.out.println(new String(buf, 0, msg));
bis.close();
s.close();
}
}
package com.heisejiuhuche.socket;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadPicByThreadServer {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10007);
while(true) {
Socket s = ss.accept();
new Thread(new PicServerThread(s)).start();
}
}
}
/* 將客戶端上傳的代碼封裝成線程 */
class PicServerThread implements Runnable {
private Socket s;
PicServerThread(Socket s) {
this.s = s;
}
public void run() {
String ip = s.getInetAddress().getHostAddress();
int count = 1;
try {
System.out.println(ip + " connected..");
/* 避免文件名重複 */
File file = new File("C://Users//jeremy//Documents//" + ip + "(" + count + ")" + ".jpg");
/* 循環判斷,如果文件存在,讓count自增 */
while(file.exists())
file = new File("C://Users//jeremy//Documents//" + ip + "(" + count++ + ")" + ".jpg");
/* 讀取文件,寫入服務端 */
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
int b = 0;
while((b = bis.read()) != -1) {
bos.write(b);
bos.flush();
}
OutputStream out = s.getOutputStream();
out.write("上傳成功".getBytes());
bos.close();
s.close();
} catch(IOException e) {
throw new RuntimeException(ip + " 上傳失敗...");
}
}
}
- 2.檢測用戶名練習
- 客戶端通過鍵盤錄入用戶名,如果該用戶存在,在服務器端顯示xxx,已登錄;在客戶端顯示xxx,歡迎光臨。如果用戶不存在,在服務端顯示xxx,嘗試登錄;在客戶端顯示xxx,該用戶不存在。
示例代碼:
package com.heisejiuhuche.socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class LoginClient {
public static void main(String[] args) throws Exception {
Socket s = new Socket("localhost", 10009);
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
for(int x = 0; x < 3; x++) {
String line = bufr.readLine();
/* 如果輸入爲空,直接結束循環 */
if(line == null)
break;
/* 發送名字到服務器端 */
pw.println(line);
/* 讀取回饋信息 */
String feedback = bufIn.readLine();
System.out.println(feedback);
/* 如果回饋信息有歡迎字樣,結束循環 */
if(feedback.contains("歡迎"))
break;
}
bufr.close();
s.close();
}
}
package com.heisejiuhuche.socket;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class LoginServer {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10009);
while(true) {
Socket s = ss.accept();
new Thread(new UserThread(s)).start();
}
}
}
class UserThread implements Runnable {
private Socket s;
UserThread(Socket s) {
this.s = s;
}
public void run() {
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip + " connected...");
try {
for(int x = 0; x < 3; x++) {
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
String name = bufIn.readLine();
if(name == null)
break;
/* 這一行代碼一定放在for循環裏面,不然readLine的指針回不到文件開端,讀不到名字 */
BufferedReader bufr = new BufferedReader(new FileReader("C://Users//jeremy//Documents//javaTmp//user.txt"));
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
String line = null;
Boolean flag = false;
/* 根據flag的結果判斷要進行的操作 */
while((line = bufr.readLine()) != null) {
if(name.equals(line)) {
flag = true;
break;
}
}
if(flag) {
System.out.println(name + "已登錄!");
pw.println(name + ", 歡迎登錄!");
break;
} else {
System.out.println(ip + "正在嘗試登錄...");
pw.println(name + "不存在...");
}
bufr.close();
}
s.close();
} catch(IOException e) {
throw new RuntimeException(ip + "校驗失敗...");
}
}
}
- 3.自定義瀏覽器
- 1)HTTP請求消息頭
- 請求頭信息中包括了發送該請求的主機地址,端口,路徑,可以接受的應用程序類型,所使用語言,封裝壓縮形式等信息。請求頭信息示例如下:
GET / HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:11000
DNT: 1
Connection: Keep-Alive
模擬一個瀏覽器,想服務器發送請求頭信息,獲取頁面。
示例代碼:
package com.heisejiuhuche.socket;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class BrowserClient {
public static void main(String[] args) throws Exception {
/* 訪問本機的Tomcat服務器,端口8080 */
Socket s = new Socket("localhost", 8080);
PrintWriter out = new PrintWriter(s.getOutputStream(), true);
/* 發送HTTP請求頭 */
out.println("GET / HTTP/1.1");
out.println("Accept: */*");
out.println("Accept-Language: en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3");
out.println("Accept-Encoding: gzip, deflate");
out.println("Connection: closed");
/* 用空行分割請求頭和請求體 */
out.println();
out.println();
/* 獲取服務器返回的信息 */
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf, 0, len));
s.close();
}
}
二、URL類
- 1.概述
URL(Uniform Resource Locator)
類代表一個同一資源定位符。該類是指向互聯網資源的指針。- 2.常用方法
- -String getFile():獲取此URL的文件名
-String getHost():獲取此URL的主機名
-String getPath():獲取此URL的路徑部分
-int getPort():獲取此URL的端口號
-String getProtocol():獲取此URL的協議名稱
-String getQuery():獲取此URL的查詢部
- 3.常用方法演示
示例代碼:
package com.heisejiuhuche.socket;
import java.net.MalformedURLException;
import java.net.URL;
public class URLDemo {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("http://192.168.0.100:8080/myweb/index.html/?name=hsjhc&age=25");
/* 演示常用方法 */
System.out.println("getProtocol: " + url.getProtocol());
System.out.println("getHost: " + url.getHost());
System.out.println("getPort: " + url.getPort());
System.out.println("getPath: " + url.getPath());
System.out.println("getFile: " + url.getFile());
System.out.println("getQuery: " + url.getQuery());
}
}
程序運行結果:
getProtocol: http
getHost: 192.168.0.100
getPort: 8080
getPath: /myweb/index.html/
getFile: /myweb/index.html/?name=hsjhc&age=25
getQuery: name=hsjhc&age=25
注意:
沒有指定端口,返回-1;開發時要進行判斷,如果port的值爲-1,要給port賦值爲80
三、URLConnection類
- 1.概述
URLConnection
類對象是URL對象調用openConnect()
方法之後的返回值,就能得到這個URL
的連接對象,也就是目標主機對象;獲取URLConnection
對象可以方便獲取Socket輸入流對象
。- 2.URLConnection類演示
示例代碼:
package com.heisejiuhuche.socket;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class URLConnectionDemo {
public static void main(String[] args) throws Exception {
/* 封裝URL對象 */
URL url = new URL("http://192.168.0.100:8080/myweb/indetml/?name=hsjhc&age=25");
/* 調用url對象的openConnection方法,得到URLConnection對象 */
URLConnection conn = url.openConnection();
System.out.println(conn);
/* 因爲URLConnection對象中封裝了Socket對象
* 所以可以調用getInputStream()方法獲取輸入流來讀取服務器端數據 */
InputStream in = conn.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf, 0, len));
}
}
注意:
url.openConnection().getInputStream()這行代碼,可以簡寫成url.openStream();該方法是URLConnection類中獲取InputStream的便捷方法
四、擴展知識
- 1.Socket空參數構造函數
- 通過調用
connect()
方法連接主機;connect()
方法可以接收一個SocketAddress
對象作爲參數;SocketAddress
類的子類InetSocketAddress
類封裝IP地址
和端口
;那麼以前在創建Socket對象
的時候分開寫主機IP和端口,現在也可以調用connect
方法,傳遞一個InetSocketAddress
對象即可。- 2.ServerSocket類接收的backlog參數
backlog
表示隊列的最大長度。它代表能同時連接到服務器的最大客戶端個數。在服務器端開發時,要指定這個參數,避免服務器因連接數過多而死機。五、域名解析
- 1.概述
IP地址
不容易記憶,通常人們在訪問網站的時候,在地址欄輸入的都是類似於www.baidu.com
這樣的域名。但是要訪問到網絡上對應這個域名的那個主機,需要將這個域名解析成IP地址。這個過程,需要DNS域名解析服務器
來完成。DNS服務器
中記錄的是每個域名對應的IP地址映射表。- 2.基本原理
PC在訪問網站的時候,要走這4步流程。在瀏覽器輸入域名之後,第1步,到本機C盤``(C:\windows\Systems\drivers\etc\)
下的hosts文件
,尋找這個域名是否在本地有已經配置的IP地址,如果有,直接訪問該IP,否則,進入第2步;第2步,訪問公網DNS服務器,找到DNS服務器上的域名——IP映射關係;第3步,DNS服務器將對應的IP地址返回給PC;第4步,PC端訪問該IP地址,找到公網上的指定主機。
注意:
127.0.0.1與localhost之間的映射關係在本機上C盤下的hosts文件中。該文件可以用於屏蔽惡意網站。