網(wǎng)絡(luò)
Socket 套接字
Socket 編程寫數(shù)據(jù)到輸出流:PrintWriter
Socket 模仿向服務(wù)端上傳文件
Socket 實現(xiàn)單對單的聊天
Socket 實現(xiàn)多對多的聊天
Http協(xié)議
Get請求 VS Post請求
URL 簡介
URL 發(fā)送Get請求
URL 發(fā)送POST請求
參考文章
一.Socket 套接字
1. java.net 包中提供了兩種常見的網(wǎng)絡(luò)協(xié)議的支持:
TCP
:TCP
是傳輸控制協(xié)議的縮寫介袜,它保障了兩個應(yīng)用程序之間的可靠通信条获。通常用于互聯(lián)網(wǎng)協(xié)議,被稱TCP / IP
嗅榕。
UDP
:UDP
是用戶數(shù)據(jù)報協(xié)議的縮寫,一個無連接的協(xié)議稼稿。提供了應(yīng)用程序之間要發(fā)送的數(shù)據(jù)的數(shù)據(jù)包册招。
2. 套接字實現(xiàn)網(wǎng)絡(luò)通信:
套接字使用TCP提供了兩臺計算機(jī)之間的通信機(jī)制。 客戶端程序創(chuàng)建一個套接字萎攒,并嘗試連接服務(wù)器的套接字遇八。
當(dāng)連接建立時矛绘,服務(wù)器會創(chuàng)建一個
Socket
對象∪杏溃客戶端和服務(wù)器現(xiàn)在可以通過對Socket
對象的寫入和讀取來進(jìn)行通信货矮。
java.net.Socket
類代表一個套接字,并且java.net.ServerSocket
類為服務(wù)器程序提供了一種來監(jiān)聽客戶端揽碘,并與他們建立連接的機(jī)制次屠。
3. 兩臺計算機(jī)之間使用套接字建立TCP連接步驟:
服務(wù)器實例化一個
ServerSocket
對象,表示通過服務(wù)器上的端口通信雳刺。服務(wù)器調(diào)用
ServerSocket
類的accept()
方法劫灶,該方法將一直等待,直到客戶端連接到服務(wù)器上給定的端口掖桦。服務(wù)器正在等待時本昏,一個客戶端實例化一個
Socket
對象,指定服務(wù)器名稱和端口號來請求連接枪汪。
Socket
類的構(gòu)造函數(shù)試圖將客戶端連接到指定的服務(wù)器和端口號涌穆。如果想建立通信,則在客戶端創(chuàng)建一個能夠與服務(wù)器進(jìn)行通信的Socket
對象雀久。在服務(wù)器端宿稀,
accept()
方法返回服務(wù)器上一個新的socket
引用,這個新的socket
表示連接到服務(wù)端的一個socket
赖捌。連接建立后祝沸,通過使用
I/O 流
在進(jìn)行通信,每一個socket
都有一個輸出流和一個輸入流越庇,客戶端的輸出流連接到服務(wù)器端的輸入流罩锐,而客戶端的輸入流連接到服務(wù)器端的輸出流。
4. ServerSocket 類的方法
方法 描述 public ServerSocket(int port) throws IOException 構(gòu)造方法:創(chuàng)建綁定到特定端口的服務(wù)器套接字卤唉。 public int getLocalPort() 返回此套接字在其上偵聽的端口 public Socket accept() throws IOException 偵聽并接受到此服務(wù)器套接字的連接 5. Socket 類的方法
方法 描述 public Socket(String host, int port) throws UnknownHostException, IOException 構(gòu)造方法:創(chuàng)建一個流套接字并將其連接到指定主機(jī)上的指定端口號 public int getPort() 返回此套接字連接到的遠(yuǎn)程端口 public int getLocalPort() 返回此套接字綁定到的本地端口 public InputStream getInputStream() throws IOException 返回此套接字的輸入流 public OutputStream getOutputStream() throws IOException 返回此套接字的輸出流 public void shutdownOutput() throws IOException 只關(guān)閉OutputStream而在關(guān)閉的同時涩惑,并不關(guān)閉網(wǎng)絡(luò)連接 public void shutdownInput() throws IOException 只關(guān)閉InputStream,而在關(guān)閉的同時桑驱,并不關(guān)閉網(wǎng)絡(luò)連接 public void close() throws IOException 關(guān)閉此套接字 6. 客戶端和服務(wù)器端數(shù)據(jù)傳輸
客戶端和服務(wù)器端讀取數(shù)據(jù):
BuffereReader->InputStreamReader->socket.getInputStream()
- 客戶端讀取服務(wù)器端的數(shù)據(jù)竭恬,socket就是創(chuàng)建用于通信的socket
- 服務(wù)器端讀取客戶端的數(shù)據(jù),socket就是連接客戶端后獲得的通信的socket
- 客戶端熬的、服務(wù)器端讀取終端數(shù)據(jù)萍聊,
BuffereReader->InputStreamReader->System.in
客戶端和服務(wù)器端輸出數(shù)據(jù):
PrintStream->socket.getOutputStream
- 客戶端輸出數(shù)據(jù)到服務(wù)器端,socket就是創(chuàng)建用于通信的socket
- 服務(wù)器端輸出數(shù)據(jù)到客戶端悦析,socket就是連接客戶端后獲得的通信的socket
二.Socket編程寫數(shù)據(jù)到輸出流:PrintStream
1. BufferedReader 和 BufferedWriter 方式
//模擬客戶端 class Client{ public static void main(String[] args) throws IOException { //創(chuàng)建用于通信的socket: // 1.指明和誰通信 ip地址 端口號 Socket socket = new Socket("127.0.0.1",8989); //接受服務(wù)端的數(shù)據(jù) BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line; while((line = bufferedReader.readLine()) != null){ System.out.println(line); } //向服務(wù)器端發(fā)送數(shù)據(jù) BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bufferedWriter.write("請求數(shù)據(jù)"); bufferedWriter.newLine(); bufferedWriter.flush(); socket.shutdownOutput();//輸出完畢寿桨,關(guān)掉輸出流,防止readLine一直等待讀 } } //模擬服務(wù)器端 class Server{ public static void main(String[] args) throws IOException { //1.創(chuàng)建服務(wù)器端的ServerSocket ServerSocket socket = new ServerSocket(8989); //2.獲取連接的客戶端的socket Socket cliensocket = socket.accept(); //3.向客戶端發(fā)送數(shù)據(jù) BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(cliensocket.getOutputStream())); bufferedWriter.write("接連服務(wù)器成功"); bufferedWriter.newLine(); bufferedWriter.flush(); cliensocket.shutdownOutput();//輸出完畢,關(guān)掉輸出流亭螟,防止readLine一直等待讀 //4.接受客戶端發(fā)送的數(shù)據(jù) BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cliensocket.getInputStream())); String line; while((line = bufferedReader.readLine()) != null){ System.out.println(line); } } }
2. BufferedReader 和 PrintStream 方式
//模擬客戶端 class Client{ public static void main(String[] args) throws IOException { //創(chuàng)建用于通信的socket: // 1.指明和誰通信 ip地址 端口號 Socket socket = new Socket("127.0.0.1",8989); //接受服務(wù)端的數(shù)據(jù) BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line; while((line = bufferedReader.readLine()) != null){ System.out.println(line); } //向服務(wù)器端發(fā)送數(shù)據(jù) PrintStream printStream = new PrintStream(cliensocket.getOutputStream()); printStream.println("連接服務(wù)器成功"); socket.shutdownOutput();//輸出完畢挡鞍,關(guān)掉輸出流,防止readLine一直等待讀 } } //模擬服務(wù)器端 class Server{ public static void main(String[] args) throws IOException { //1.創(chuàng)建服務(wù)器端的ServerSocket ServerSocket socket = new ServerSocket(8989); //2.獲取連接的客戶端的socket Socket cliensocket = socket.accept(); //3.向客戶端發(fā)送數(shù)據(jù) PrintStream printStream = new PrintStream(cliensocket.getOutputStream()); printStream.println("連接服務(wù)器成功"); cliensocket.shutdownOutput();//輸出完畢预烙,關(guān)掉輸出流墨微,防止readLine一直等待讀 //4.接受客戶端發(fā)送的數(shù)據(jù) BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cliensocket.getInputStream())); String line; while((line = bufferedReader.readLine()) != null){ System.out.println(line); } } }
3. BufferedWriter 和 PrintStream 的詳細(xì)使用:
BufferedWriter
:將文本寫入字符輸出流,緩沖各個字符從而提供單個字符扁掸。通過write()
方法可以將獲取到的字符輸出翘县,然后通過newLine()
進(jìn)行換行操作或者加上\n
。BufferedWriter
中的字符流必須通過調(diào)用flush
方法才能將其刷出去谴分。BufferedWriter
只能對字符流進(jìn)行操作锈麸,如果要對字節(jié)流操作,則使用BufferedInputStream
牺蹄。
PrintStream
:向文本輸出流打印對象的格式化表示形式忘伞。PrintStream
相對于BufferedWriter
的好處在于,PrintStream
中的字符流在遇到\n
自動刷新沙兰,或者調(diào)用println
也可以自動刷新氓奈,PrintStream
只能對字節(jié)流進(jìn)行操作,如果要對字節(jié)流操作鼎天,則使用PrintWriter
舀奶。但是PrintWriter
想要自動刷新需要在構(gòu)造方法中設(shè)置相關(guān)參數(shù),或者手動調(diào)用flush
刷新斋射。服務(wù)器接收不到客戶端的信息:客戶端中育勺,
write()
的時候如果沒有發(fā)送換行標(biāo)識符,那么服務(wù)器在接收的時候用readLine()
就讀取不出來绩鸣。因為readLine()
是讀取一行怀大,沒遇到換行就讀取不出來纱兑。Socket編程中,盡量用
PrintStream
取代BufferedWrite
呀闻。
三.Socket 模仿向服務(wù)端上傳文件
客戶端和服務(wù)端連接成功后
客戶端將文件數(shù)據(jù)寫入到內(nèi)存中后,將文件的內(nèi)容一點(diǎn)點(diǎn)傳給服務(wù)器
服務(wù)端在內(nèi)存中接收到文件數(shù)據(jù)后潜慎,將文件數(shù)據(jù)一點(diǎn)點(diǎn)寫出到服務(wù)器端
class Client{ public static void main(String[] args) throws IOException { //連接服務(wù)器 獲取socket Socket socket = new Socket("127.0.0.1", 8686); //創(chuàng)建服務(wù)端對應(yīng)的輸入流用于接受服務(wù)器端發(fā)來的數(shù)據(jù) BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); System.out.println(bufferedReader.readLine()); //向服務(wù)端發(fā)送文件(圖片) //1.將文件寫入到內(nèi)存 String path = "C:\\Users\\a2867\\Desktop\\壁紙\\1.jpg"; FileInputStream fis = new FileInputStream(path); //2.將內(nèi)容輸出到服務(wù)器端 //將文件的內(nèi)容一點(diǎn)點(diǎn)傳輸給服務(wù)器 BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); byte[] buf = new byte[1024]; int len; while((len = fis.read(buf)) != -1){ bos.write(buf,0,len); } socket.shutdownOutput(); } } class Server{ public static void main(String[] args) throws IOException { //創(chuàng)建服務(wù)器端的ServerSocket ServerSocket serverSocket = new ServerSocket(8686); //監(jiān)聽客戶端連接 //當(dāng)有客戶端來連接這個服務(wù)器捡多,就可以得到對應(yīng)的socket //當(dāng)沒有客戶端來連接,服務(wù)器一直在這里等待 Socket accept = serverSocket.accept(); //創(chuàng)建客戶端對應(yīng)的輸出流 用于向這個客戶端發(fā)送數(shù)據(jù) PrintStream printStream = new PrintStream(accept.getOutputStream()); printStream.println("連接服務(wù)器成功"); accept.shutdownOutput(); //接受客戶端傳遞過來的圖片 BufferedInputStream bis = new BufferedInputStream(accept.getInputStream()); //文件對應(yīng)的輸出流 String path = "C:\\Users\\a2867\\Desktop\\love.jpg"; FileOutputStream fos = new FileOutputStream(path); byte[] buf = new byte[1024]; int len; while((len = bis.read(buf)) != -1){ fos.write(buf,0,len); } } }
四. Socket實現(xiàn)單對單的聊天
客戶端:
- 主線程:接收終端輸入 將終端輸入發(fā)送給服務(wù)器端
- 子線程:接受服務(wù)端發(fā)過來的數(shù)據(jù)
服務(wù)端:
- 主線程:接收終端輸入 將終端輸入發(fā)送給客戶端
- 子線程:接受服務(wù)端發(fā)過來的數(shù)據(jù)
//客戶端
class Client{
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",8989);
//用一個子線程處理服務(wù)器端數(shù)據(jù)
new AcceptData(socket).start();
//終端輸入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//socket輸出流
PrintStream ps = new PrintStream(socket.getOutputStream());
//讀取終端的輸入 將輸入輸出給服務(wù)器端
String line;
while ((line = br.readLine()) != null){
ps.println(line);
}
}
}
//Server
class Server{
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8989);
//獲取連接的客戶端的socket
Socket accept = serverSocket.accept();
//創(chuàng)建子線程 處理客戶端輸入信息
new AcceptData(accept).start();
//終端輸入流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//鏈接的socket輸出流
PrintStream ps = new PrintStream(accept.getOutputStream());
//讀取終端的輸入 將輸入輸出給客戶端
String line;
while ((line = br.readLine()) != null){
ps.println(line);
}
}
}
//線程:處理對方傳輸?shù)臄?shù)據(jù)
class AcceptData extends Thread{
private Socket mSocket;
//保存Socket對象
public AcceptData(Socket socket){
mSocket = socket;
}
@Override
public void run() {
BufferedReader br = null;
try {
//獲取服務(wù)器端的輸入流對象
br = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
//讀取數(shù)據(jù)
String line ;
while ((line = br.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("網(wǎng)絡(luò)出錯");
System.exit(-1);
}finally {
try {
if (br != null){
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
if (mSocket != null) {
try {
mSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
五. Socket實現(xiàn)多對多聊天
實現(xiàn)功能:登錄铐炫,群聊垒手,私聊
需要多個客戶端連接服務(wù)器端,客戶端發(fā)送消息會被服務(wù)器端接受倒信。服務(wù)器端分析客戶端發(fā)送過來的消息科贬,做出相應(yīng)的抉擇私聊還是群發(fā)。
多個客戶端連接服務(wù)器端就需要服務(wù)器端可以接受發(fā)起連接的客戶端并保存。
客戶端發(fā)送的消息要經(jīng)過統(tǒng)一規(guī)范發(fā)送到服務(wù)器端榜掌,服務(wù)器端才能分析出來优妙。
客戶端的需求在發(fā)送的字符里面體現(xiàn),統(tǒng)一的規(guī)則如下:
登錄:u+登錄名u+
私聊:p+私聊的登錄名&發(fā)送的內(nèi)容p+
群聊:a+群聊內(nèi)容a+
/**
* 管理所有的常量變量
*/
public interface ChatProtocol {
//登錄
String LOGIN_FLAG = "u+";
//私聊
String PRIVATE_FLAG = "p+";
//群聊
String PUBLIC_FLAG = "a+";
//分隔符
String SPLITE_FLAG = "&";
//成功的狀態(tài)
String SUCCESS = "登陸成功";
//失敗的狀態(tài)
String FAILURE = "已經(jīng)登錄了";
}
/**
* 管理所有的登錄用戶(Map)
* 判斷某個用戶是否已經(jīng)登錄
*/
public class UserManager {
//保存所有用戶信息
private Map<String, Socket> users = new HashMap<>();
/**
* 判斷用戶是否已經(jīng)登錄
* @param name 用戶名
* @return 是否登錄
*/
public synchronized boolean isLogin(String name){
//遍歷key數(shù)組
for (String userName : users.keySet()) {
if (userName.equals(name)){
return true;
}
}
return false;
}
/**
* 保存用戶信息
* @param name 用戶名
* @param socket 用戶socket
*/
public synchronized void saveUserMessage(String name,Socket socket){
users.put(name,socket);
}
/**
* 通過用戶找到對應(yīng)的Socket
* @param name 用戶名
* @return Socket對象
*/
public synchronized Socket getSocketByName(String name){
//默認(rèn)連接
return users.get(name);
}
/**
* 通過Socket找到對應(yīng)的用戶
* @param socket socket
* @return 用戶名
*/
public synchronized String getNameBySocket(Socket socket){
//默認(rèn)連接
for (String key : users.keySet()) {
//取出這個key對應(yīng)的socket
if(socket == users.get(key)){
return key;
}
}
return null;
}
/**
* 獲取所有用戶的socket
* @return 所有socket的集合
*/
public synchronized Collection<Socket> getAllUsers(){
return users.values();
}
}
/**
* 服務(wù)端
*/
public class Server {
//用戶管理者
public static UserManager manager = new UserManager();
public static void main(String[] args) {
//創(chuàng)建一個ServerSocket
try(ServerSocket serverSocket = new ServerSocket(8888)) {
//監(jiān)聽所有來鏈接的客戶端
while(true){
Socket accept = serverSocket.accept();
//讓子線程處理這個Socket
new ServerThread(accept).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ServerThread extends Thread{
private Socket mSocket;
public ServerThread(Socket socket){
mSocket = socket;
}
@Override
public void run() {
//登錄 私聊 群聊 發(fā)文件
BufferedReader br = null;
PrintStream ps = null;
try {
//得到對應(yīng)的輸入流對象
br = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
//得到對應(yīng)的輸出流對象
ps = new PrintStream(mSocket.getOutputStream());
String line = null;
while((line = br.readLine()) != null){
//登錄 u+...u+
if (line.startsWith(ChatProtocol.LOGIN_FLAG) && line.endsWith(ChatProtocol.LOGIN_FLAG)){
//分割
//String[] items = line.substring(2).split(ChatProtocol.LOGIN_FLAG);
//String name = items[0];
//u+jacku+ 2 6
//System.out.println(line);
String name = line.substring(2,line.length()-2);
//System.out.println(name);
if (Server.manager.isLogin(name)){
//登陸過了
//返回結(jié)果給客戶端
ps.println(ChatProtocol.FAILURE);
}else {
//沒有登錄
ps.println(ChatProtocol.SUCCESS);
//保存當(dāng)前登錄的用戶信息
Server.manager.saveUserMessage(name,mSocket);
System.out.println(name+"連接服務(wù)器了");
}
}else if (line.startsWith(ChatProtocol.PRIVATE_FLAG) && line.endsWith(ChatProtocol.PRIVATE_FLAG)){
//p+jack&hellop+
//獲取信息
String msg = line.substring(2,line.length()-2);
//用戶名
String[] items = msg.split(ChatProtocol.SPLITE_FLAG);
String name = items[0];
String content = items[1];
//通過用戶名找到對應(yīng)的socket
Socket socketName = Server.manager.getSocketByName(name);
PrintStream psPri = new PrintStream(socketName.getOutputStream());
//獲取當(dāng)前用戶的名稱
String currentName = Server.manager.getNameBySocket(mSocket);
//發(fā)送私聊消息
psPri.println(currentName+"向你發(fā)來私聊"+content);
}else if (line.startsWith(ChatProtocol.PUBLIC_FLAG) && line.endsWith(ChatProtocol.PUBLIC_FLAG)){
//群聊
//獲取信息
String msg = line.substring(2,line.length()-2);
//獲取用戶名稱
String currentName = Server.manager.getNameBySocket(mSocket);
//遍歷所有用戶信息
Collection<Socket> sockets = Server.manager.getAllUsers();
for (Socket socket : sockets) {
PrintStream temp = new PrintStream(socket.getOutputStream());
temp.println(currentName+":"+msg);
//temp.close();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 用戶 可以復(fù)制多個該文件用作測試 模擬多個客戶端
*/
public class Client {
public static void main(String[] args) {
//接受終端輸入信息
BufferedReader br = null;
PrintStream ps = null;
BufferedReader brr = null;
//創(chuàng)建Socket 連接服務(wù)器
try(Socket socket = new Socket("127.0.0.1",8888)) {
//登錄
while (true){
//接受終端輸入流
br = new BufferedReader(new InputStreamReader(System.in));
//發(fā)給服務(wù)器輸出流
ps = new PrintStream(socket.getOutputStream());
//接受服務(wù)器輸入流
brr = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//接受終端輸入信息
String line = JOptionPane.showInputDialog("請輸入用戶名:");
//拼接登錄格式
String loginString = ChatProtocol.LOGIN_FLAG+line+ChatProtocol.LOGIN_FLAG;
//發(fā)送給服務(wù)器端
ps.println(loginString);
//接受服務(wù)器端返回的結(jié)果
String result = brr.readLine();
if (result.equals(ChatProtocol.SUCCESS)){
System.out.println("登陸成功");
break;
}else {
System.out.println("用戶名已存在憎账,請重新登錄");
}
}
//登陸成功
//開啟線程 處理服務(wù)器端的輸入
new ClientThread(socket).start();
//接受終端輸入發(fā)送給服務(wù)器端
//從終端輸入信息
String line = null;
while((line = br.readLine()) != null){
//發(fā)送給服務(wù)器
ps.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ClientThread extends Thread{
private Socket mSocket;
public ClientThread(Socket socket){
mSocket = socket;
}
@Override
public void run() {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
System.out.println("網(wǎng)絡(luò)出錯");
}finally {
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
六. Http協(xié)議
Http簡介
HTTP協(xié)議是Hyper Text Transfer Protocol(超文本傳輸協(xié)議)的縮寫,是用于從萬維網(wǎng)(WWW:World Wide Web )服務(wù)器傳輸超文本到本地瀏覽器的傳送協(xié)議套硼。
HTTP是一個基于TCP/IP通信協(xié)議來傳遞數(shù)據(jù)(HTML 文件, 圖片文件, 查詢結(jié)果等)。
一個完整的http請求需要經(jīng)歷兩個過程:客戶端發(fā)送請求到服務(wù)器胞皱,然后服務(wù)器將結(jié)果返回給客戶端
1. 客戶端->服務(wù)端
客戶端向服務(wù)器發(fā)送請求主要包含以下信息:請求的URL地址邪意、請求頭以及可選的請求體
請求URL(Request URL)
上圖中的Request URL就是請求的URL地址,即https://www.baidu.com反砌,該URL沒有附加其他的參數(shù)雾鬼。
其實可以通過 ? 和 & 符號向URL地址后面追加一系列的鍵值對參數(shù),比如地址https://www.baidu.com/s?ie=utf-8&wd=Android于颖,該URL包含兩個鍵值對,ie=utf-8呆贿,以及wd=Android,ie和wd是key森渐,utf-8和Android分別是其對應(yīng)的value做入,服務(wù)端可以獲取ie和wd所對應(yīng)的value的值。
由此我們可以看出同衣,URL可以攜帶額外的數(shù)據(jù)信息竟块。一般情況下,URL的長度不能超過2048個字符耐齐,即2KB浪秘,超過此限制的話服務(wù)器可能就不識別。
請求頭(Request Headers)
上圖中Request Headers部分就是請求頭埠况,請求頭其實也是一些鍵值對耸携,不過這些鍵值通常都是W3C定義了的一些標(biāo)準(zhǔn)的Http請求頭的名稱,請求頭包含了客戶端想告訴服務(wù)端的一些元數(shù)據(jù)信息辕翰。
注意是元數(shù)據(jù)夺衍,而不是數(shù)據(jù),比如請求頭
User-Agent
會告訴服務(wù)器這條請求來自于什么瀏覽器喜命,再比如請求頭Accept-Encoding
會告訴服務(wù)器客戶端支持的壓縮格式沟沙。除了這些標(biāo)準(zhǔn)的請求頭,我們還可以添加自定義的請求頭壁榕。請求體(Request Body)
之前我們提到矛紫,URL的最大長度就是2048個字符,如果我們發(fā)送的數(shù)據(jù)很大牌里,超過了2KB怎么辦颊咬?我們可以將很大的數(shù)據(jù)放到請求體中,GET請求不支持請求體,只有POST請求才能設(shè)置請求體喳篇。
請求體中可以放置任意的字節(jié)流缓呛,從而可以很方便地發(fā)送任意格式的數(shù)據(jù),服務(wù)端只需要讀取該輸入流即可杭隙。
分析圖
2. 服務(wù)器->客戶端
服務(wù)器接收到客戶端發(fā)來的請求后哟绊,會進(jìn)行相應(yīng)的處理,并向客戶端輸出信息痰憎,輸出的信息包括響應(yīng)頭和響應(yīng)體票髓。
響應(yīng)頭
響應(yīng)頭也是一些鍵值對,包含了服務(wù)器想要告訴客戶端的一些元數(shù)據(jù)信息铣耘,注意不是數(shù)據(jù)洽沟,是元數(shù)據(jù)。
比如通過響應(yīng)頭
Content-Encoding
告訴客戶端服務(wù)器所采用的壓縮格式蜗细,響應(yīng)頭Content-Type
告訴客戶端響應(yīng)體是什么格式的數(shù)據(jù)裆操,再比如服務(wù)端可以通過多個Set-Cookie
響應(yīng)頭向客戶端寫入多條Cookie信息,等等炉媒。剛剛提到的幾個請求頭都是W3C規(guī)定的標(biāo)準(zhǔn)的請求頭名稱踪区,我們也可以在服務(wù)端向客戶端寫入自定義的響應(yīng)頭。
響應(yīng)體
- 響應(yīng)體是服務(wù)端向客戶端傳輸?shù)膶嶋H的數(shù)據(jù)信息吊骤,本質(zhì)就是一堆字節(jié)流缎岗,可以表示文本,也可以表示圖片或者其他格式的信息白粉。
分析圖
七.Get請求 VS Post請求
Http協(xié)議支持的操作有
GET
传泊、POST
、HEAD
鸭巴、PUT
眷细、TRACE
、OPTIONS
鹃祖、DELETE
溪椎,其中最最常用的還是GET和POST操作。1. GET請求:
GET請求可以被緩存惯豆。
當(dāng)發(fā)送鍵值對信息時池磁,可以在URL上面直接追加鍵值對參數(shù)奔害。當(dāng)用GET請求發(fā)送鍵值對時楷兽,鍵值對會隨著URL一起發(fā)送的。
由于GET請求發(fā)送的鍵值對時隨著URL一起發(fā)送的华临,所以一旦該URL被黑客截獲芯杀,那么就能看到發(fā)送的鍵值對信息,所以GET請求的安全性很低,不能用GET請求發(fā)送敏感的信息(比如用戶名密碼)揭厚。
由于URL不能超過2048個字符却特,所以GET請求發(fā)送數(shù)據(jù)是有長度限制的。
由于GET請求較低的安全性筛圆,我們不應(yīng)該用GET請求去執(zhí)行增加裂明、刪除、修改等的操作太援,應(yīng)該只用它獲取數(shù)據(jù)闽晦。
2. POST請求:
POST請求從不會被緩存。
POST請求的URL中追加鍵值對參數(shù)提岔,不過這些鍵值對參數(shù)不是隨著URL發(fā)送的仙蛉,而是被放入到請求體中發(fā)送的,這樣安全性稍微好一些碱蒙。
應(yīng)該用POST請求發(fā)送敏感信息荠瘪,而不是用GET。
由于可以在請求體中發(fā)送任意的數(shù)據(jù)赛惩,所以理論上POST請求不存在發(fā)送數(shù)據(jù)大小的限制哀墓。
當(dāng)執(zhí)行增減、刪除喷兼、修改等操作時麸祷,應(yīng)該使用POST請求,而不應(yīng)該使用GET請求褒搔。
八.URL
1.URL簡介
URL(Uniform Resource Locator)
中文名為統(tǒng)一資源定位符阶牍,有時也被俗稱為網(wǎng)頁地址,表示為互聯(lián)網(wǎng)上的資源星瘾。格式:
protocol://host:port/path?variable=value&variable=value...
分析的例子:http://127.0.0.1/login.php?user_name=jack&user_password=123
- 協(xié)議為(protocol):如http
- 主機(jī)為(host:port):127.0.0.1
- 端口號為(port): 80 走孽,上面的URL并未指定端口,因為 HTTP 協(xié)議默認(rèn)的端口號為 80
- 文件路徑為(path):/login.php
- 請求參數(shù)(variable=value):user_name=jack&user_password=123
2.URL類的方法
方法 描述 public URL(String url) throws MalformedURLException 構(gòu)造方法:通過給定的URL字符串創(chuàng)建URL public String getPath() 返回URL路徑部分 public String getQuery() 返回URL查詢部分 public int getPort() 返回URL端口部分 public String getHost() 返回URL的主機(jī) public String getFile() 返回URL文件名部分 public URLConnection openConnection() throws IOException 打開一個URL連接琳状,并運(yùn)行客戶端訪問資源 3.URLConnections 類的方法
方法 描述 Object getContent() 檢索URL鏈接內(nèi)容 String getContentEncoding() 返回頭部 content-encoding 字段值 int getContentLength() 返回頭部 content-length字段值 String getContentType() 返回頭部 content-type 字段值 public void setDoInput(boolean input) URL 連接可用于輸入和/或輸出磕瓷。如果打算使用 URL 連接進(jìn)行輸入,則將 DoInput 標(biāo)志設(shè)置為 true念逞;如果不打算使用届案,則設(shè)置為 false。默認(rèn)值為 true亿笤。 public void setDoOutput(boolean output) URL 連接可用于輸入和/或輸出衙伶。如果打算使用 URL 連接進(jìn)行輸出,則將 DoOutput 標(biāo)志設(shè)置為 true叨咖;如果不打算使用瘩例,則設(shè)置為 false啊胶。默認(rèn)值為 false。 public InputStream getInputStream() throws IOException 返回URL的輸入流垛贤,用于讀取資源 public OutputStream getOutputStream() throws IOException 返回URL的輸出流, 用于寫入資源 public URL getURL() 返回 URLConnection 對象連接的URL 4.HttpURLConnection類(URLConnection的子類)的方法
方法 描述 public void setRequestMethod(String method) throws ProtocolException 設(shè)置請求方式焰坪,默認(rèn)方式是GET public int getResponseCode() throws IOException 請求的狀態(tài)碼,一個int代表三位HTTP狀態(tài)代碼聘惦。1xx:信息 某饰;2xx:成功 ;3xx:重定向 善绎;4xx:客戶端錯誤 露乏;5xx:服務(wù)器錯誤 public String getResponseMessage() throws IOException HTTP響應(yīng)信息 5. 簡單的后臺文件
login.php 文件
<?PHP #獲取用戶輸入的姓名和密碼 $name = $_GET["user_name"]; $pwd = $_GET["user_password"]; $user = array( "name"=>$name, "password"=>$pwd, ); $result = array( "user"=>$user, "total"=>"2", "status"=>0, ); #規(guī)定返回類型數(shù)據(jù)為JSON數(shù)據(jù) header('Content-Type:application/json'); echo json_encode($result); ?>
loginPOST.php 文件
<?PHP #獲取用戶輸入的姓名和密碼 $name = $_POST["user_name"]; $pwd = $_POST["user_password"]; $user = array( "name"=>$name, "password"=>$pwd, ); $result = array( "user"=>$user, "total"=>"2", "status"=>0, ); #規(guī)定返回類型數(shù)據(jù)為JSON數(shù)據(jù) header('Content-Type:application/json'); echo json_encode($result); ?>
upLoadFile.php 文件
<?PHP //獲取文件 $file = $_FILES["file"]; //獲取文件信息 if($file["error"] > 0){ //讀取文件出錯 echo "Error:".$file["error"]."<br/>"; }else{ //輸出詳細(xì)信息 echo "上傳的文件名:".$file["name"]."<br/>"; echo "上傳的文件類型:".$file["type"]."<br/>"; echo "上傳的文件大小:".($file["size"]/1024)."Kb<br/>"; echo "臨時路徑:".$file["tmp_name"]."<br/>"; //判斷文件類型 $type = $file["type"]; $path; if($type == "image/jpeg" || $type == "image/png"){ //圖片 $path = "upLoad/img/"; }else if($type == "video/mp4"){ //視頻 $path = "upLoad/video/"; }else if($type == "text/plain"){ //文本 $path = "upLoad/file/"; } } $filePath = $path.$file["name"]; //判斷文件是否存在 if(file_exists($filePath)){ //存在 echo $file["name"]."已存在"; }else{ //不存在 //將臨時文件里面的文件移動到指定目錄 move_uploaded_file($file["tmp_name"],$filePath); echo "文件已保存在:".$filePath; } ?>
界面提交返回JSON數(shù)據(jù)的運(yùn)行結(jié)果
上傳和下載文件返回結(jié)果數(shù)據(jù)的運(yùn)行結(jié)果
九.URL 發(fā)送Get請求
1.GET方式請求:帶參數(shù)的下載
public static void getParam() throws IOException { //1.創(chuàng)建URL String path = "http://127.0.0.1/login.php?user_name=jack&user_password=123"; URL url = new URL(path); //2.創(chuàng)建請求方式 獲取鏈接的對象 URLConnection封裝了Socket 默認(rèn)的請求方式是Get URLConnection urlConnection = url.openConnection(); //HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); //urlConnection.setRequestMethod("GET"); //查看請求狀態(tài) //System.out.println(urlConnection.getResponseCode()); //3.發(fā)送數(shù)據(jù)-就在URL里面 //4.接受服務(wù)器端的數(shù)據(jù) 一行一行的讀 BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); System.out.println(br.readLine()); }
2.GET方式請求:不帶參數(shù)下載
public static void getImage() throws IOException { //創(chuàng)建URL URL url = new URL("http://127.0.0.1/upLoad/img/1.jpg"); //獲取與服務(wù)器連接的對象 URLConnection urlConnection = url.openConnection(); //讀取下載的內(nèi)容-獲取輸入流 InputStream is = urlConnection.getInputStream(); //創(chuàng)建文件保存的位置 FileOutputStream fos = new FileOutputStream(""C:\\Users\\a2867\\Desktop\\1.jpg""); byte[] buf = new byte[1024]; int len; while ((len = is.read(buf)) != -1){ fos.write(buf,0,len); } }
3.Get 請求總結(jié)
Get請求常常被用來下載數(shù)據(jù),既可以下載服務(wù)器端響應(yīng)的數(shù)據(jù)涂邀,也可以下載具體的文件瘟仿。
Get請求也可以用來上傳數(shù)據(jù),但是Get請求是通過 "key=value" 的鍵值對格式添加到URL后面的形式上傳數(shù)據(jù)比勉,由于沒有請求體導(dǎo)致上傳的數(shù)據(jù)大小受到URL長度限制劳较。
十.URL 發(fā)送Post請求
1. POST方式上傳 "key=value" 的鍵值對數(shù)據(jù)
//POST方式上傳數(shù)據(jù) public static void postParam() throws IOException { //1.創(chuàng)建url URL url = new URL("http://127.0.0.1/loginPOST.php"); //2.獲取連接對象 //HttpURLConnection的父類URLConnection //需要設(shè)定請求的內(nèi)容(請求方式,上傳內(nèi)容)用HttpURLConnection HttpURLConnection collection = (HttpURLConnection)url.openConnection(); //3.設(shè)置請求方式為POST collection.setRequestMethod("POST"); //設(shè)置有輸出流 需要上傳 collection.setDoOutput(true); //設(shè)置有輸入流 需要下載 默認(rèn)true collection.setDoInput(true); //4.準(zhǔn)備上傳的數(shù)據(jù) String data = "user_name=jack&user_password=123"; //5.開始上傳 輸出流對象 OutputStream os = collection.getOutputStream(); os.write(data.getBytes()); os.flush(); os.close(); //6.接受服務(wù)器端返回數(shù)據(jù) BufferedReader br = new BufferedReader(new InputStreamReader(collection.getInputStream())); System.out.println(br.readLine()); }
2. POST方式下載圖片
public static void postForImg() throws IOException { //創(chuàng)建URL URL url = new URL("http://127.0.0.1/upLoad/img/1.jpg"); //獲取與服務(wù)器連接的對象 HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection(); urlConnection.setRequestMethod("POST"); //讀取下載的內(nèi)容-獲取輸入流 InputStream is = urlConnection.getInputStream(); //創(chuàng)建文件保存的位置 FileOutputStream fos = new FileOutputStream("C:\\Users\\a2867\\Desktop\\1.jpg"); byte[] buf = new byte[1024]; int len; while ((len = is.read(buf)) != -1){ fos.write(buf,0,len); } }
3. POST方式上傳 txt 文件
//POST方式上傳文件-文本 public static void postFile() throws IOException { //1.創(chuàng)建url URL url = new URL("http://127.0.0.1/upLoadFile.php"); //2.獲取連接對象 //HttpURLConnection的父類URLConnection //需要設(shè)定請求的內(nèi)容(請求方式浩聋,上傳內(nèi)容)用HttpURLConnection HttpURLConnection collection = (HttpURLConnection)url.openConnection(); //3.設(shè)置請求方式為POST collection.setRequestMethod("POST"); //設(shè)置有輸出流 需要上傳 collection.setDoOutput(true); //設(shè)置有輸入流 需要下載 默認(rèn)true collection.setDoInput(true); //上傳格式 final String newLine = "\r\n"; final String boundaryPrefix = "--"; String boundary = "ABC"; //collection.setRequestProperty("connection","Keep-Alive"); //collection.setRequestProperty("Charset","UTF-8"); collection.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary); StringBuilder sb = new StringBuilder(); sb.append(boundaryPrefix); sb.append(boundary); sb.append(newLine); sb.append("Content-Disposition: form-data;name=\"file\";filename=\"test.txt\"" + newLine); sb.append("Content-Type:text/plain" + newLine); sb.append(newLine); OutputStream out = new DataOutputStream(collection.getOutputStream()); out.write(sb.toString().getBytes());// 將參數(shù)頭的數(shù)據(jù)寫入到輸出流中 //4.準(zhǔn)備上傳的數(shù)據(jù) InputStream bis = new FileInputStream("C:\\Users\\a2867\\Desktop\\開發(fā)者文檔.txt"); //5.開始上傳 輸出流對象 byte[] buf = new byte[1024]; while ((bis.read(buf)) != -1){ out.write(buf,0,buf.length); } out.write(newLine.getBytes()); bis.close(); byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine).getBytes(); out.write(end_data); out.flush(); out.close(); //6.接受服務(wù)器端返回數(shù)據(jù)-響應(yīng) BufferedReader br = new BufferedReader(new InputStreamReader(collection.getInputStream())); System.out.println(br.readLine()); }
4. POST方式上傳 圖片 文件
//POST方式上傳文件-文本 public static void postFile() throws IOException { //1.創(chuàng)建url URL url = new URL("http://127.0.0.1/upLoadFile.php"); //2.獲取連接對象 //HttpURLConnection的父類URLConnection //需要設(shè)定請求的內(nèi)容(請求方式观蜗,上傳內(nèi)容)用HttpURLConnection HttpURLConnection collection = (HttpURLConnection)url.openConnection(); //3.設(shè)置請求方式為POST collection.setRequestMethod("POST"); //設(shè)置有輸出流 需要上傳 collection.setDoOutput(true); //設(shè)置有輸入流 需要下載 默認(rèn)true collection.setDoInput(true); //上傳格式 final String newLine = "\r\n"; final String boundaryPrefix = "--"; String boundary = "ABC"; //collection.setRequestProperty("connection","Keep-Alive"); //collection.setRequestProperty("Charset","UTF-8"); collection.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary); StringBuilder sb = new StringBuilder(); sb.append(boundaryPrefix); sb.append(boundary); sb.append(newLine); sb.append("Content-Disposition: form-data;name=\"file\";filename=\"test.jpeg\"" + newLine); sb.append("Content-Type:image/jpeg" + newLine); sb.append(newLine); OutputStream out = new DataOutputStream(collection.getOutputStream()); out.write(sb.toString().getBytes());// 將參數(shù)頭的數(shù)據(jù)寫入到輸出流中 //4.準(zhǔn)備上傳的數(shù)據(jù) InputStream bis = new FileInputStream("C:\\Users\\a2867\\Desktop\\壁紙\\3.jpeg"); //5.開始上傳 輸出流對象 byte[] buf = new byte[1024]; while ((bis.read(buf)) != -1){ out.write(buf,0,buf.length); } out.write(newLine.getBytes()); bis.close(); byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine).getBytes(); out.write(end_data); out.flush(); out.close(); //6.接受服務(wù)器端返回數(shù)據(jù)-響應(yīng) BufferedReader br = new BufferedReader(new InputStreamReader(collection.getInputStream())); System.out.println(br.readLine()); }
5. POST方式上傳 視頻 文件
//POST方式上傳文件-文本 public static void postFile() throws IOException { //1.創(chuàng)建url URL url = new URL("http://127.0.0.1/upLoadFile.php"); //2.獲取連接對象 //HttpURLConnection的父類URLConnection //需要設(shè)定請求的內(nèi)容(請求方式,上傳內(nèi)容)用HttpURLConnection HttpURLConnection collection = (HttpURLConnection)url.openConnection(); //3.設(shè)置請求方式為POST collection.setRequestMethod("POST"); //設(shè)置有輸出流 需要上傳 collection.setDoOutput(true); //設(shè)置有輸入流 需要下載 默認(rèn)true collection.setDoInput(true); //上傳格式 final String newLine = "\r\n"; final String boundaryPrefix = "--"; String boundary = "ABC"; //collection.setRequestProperty("connection","Keep-Alive"); //collection.setRequestProperty("Charset","UTF-8"); collection.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary); StringBuilder sb = new StringBuilder(); sb.append(boundaryPrefix); sb.append(boundary); sb.append(newLine); sb.append("Content-Disposition: form-data;name=\"file\";filename=\"test.mp4\"" + newLine); sb.append("Content-Type:video/mp4" + newLine); sb.append(newLine); OutputStream out = new DataOutputStream(collection.getOutputStream()); out.write(sb.toString().getBytes());// 將參數(shù)頭的數(shù)據(jù)寫入到輸出流中 //4.準(zhǔn)備上傳的數(shù)據(jù) InputStream bis = new FileInputStream("C:\\Users\\a2867\\Desktop\\壁紙\\testVideo.mp4"); //5.開始上傳 輸出流對象 byte[] buf = new byte[1024]; while ((bis.read(buf)) != -1){ out.write(buf,0,buf.length); } out.write(newLine.getBytes()); bis.close(); byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine).getBytes(); out.write(end_data); out.flush(); out.close(); //6.接受服務(wù)器端返回數(shù)據(jù)-響應(yīng) BufferedReader br = new BufferedReader(new InputStreamReader(collection.getInputStream())); System.out.println(br.readLine()); }
6. POST請求總結(jié)
POST請求常用來上傳文件數(shù)據(jù)衣洁,下載數(shù)據(jù)一般用GET請求墓捻。
POST請求和GET請求一樣都是可以用來提交表單數(shù)據(jù),提交表單數(shù)據(jù)的時候默認(rèn)類型是 "application/x-www-form-urlencoded" 也就是key=value的鍵值對格式坊夫。
"multipart/from-data" 和 "application/x-www-form-urlencoded" 一樣都是一種進(jìn)行表單提交時的消息格式砖第,"multipart/from-data" 用于提交文件類型數(shù)據(jù)。
7.POST請求報文格式
8.文件對應(yīng)的 MIMEType
類型 文件拓展名 MIMEType 文本 js application/javascript 文本 application/pdf 文本 text/txt text/plain 文本 json application/json 文本 xml text/xml 圖片 png image/png 圖片 bmp\dip image/bmp 圖片 jpe\jpeg\jpg image/jpeg 圖片 gif image/gif 多媒體 mp3 audio/mpeg 多媒體 mp4\mpg4\m4v\mp4v video/mp4 9. 上傳方法總結(jié)
public static void postFileToServer(String serverURL,String postKey,String fileName,String fileType,String myPath) throws IOException { //1.創(chuàng)建url URL url = new URL(serverURL); //2.獲取連接對象 //HttpURLConnection的父類URLConnection //需要設(shè)定請求的內(nèi)容(請求方式环凿,上傳內(nèi)容)用HttpURLConnection HttpURLConnection collection = (HttpURLConnection)url.openConnection(); //3.設(shè)置請求方式為POST collection.setRequestMethod("POST"); //設(shè)置有輸出流 需要上傳 collection.setDoOutput(true); //設(shè)置有輸入流 需要下載 默認(rèn)true collection.setDoInput(true); //上傳格式 final String newLine = "\r\n"; final String boundaryPrefix = "--"; String boundary = "ABC"; //collection.setRequestProperty("connection","Keep-Alive"); //collection.setRequestProperty("Charset","UTF-8"); collection.setRequestProperty("Content-Type","multipart/form-data;boundary="+boundary); StringBuilder sb = new StringBuilder(); sb.append(boundaryPrefix); sb.append(boundary); sb.append(newLine); sb.append("Content-Disposition: form-data;name=\""+postKey+"\";filename=\""+fileName+"\"" + newLine); sb.append("Content-Type:" + fileType + newLine); sb.append(newLine); OutputStream out = new DataOutputStream(collection.getOutputStream()); out.write(sb.toString().getBytes());// 將參數(shù)頭的數(shù)據(jù)寫入到輸出流中 //4.準(zhǔn)備上傳的數(shù)據(jù) InputStream bis = new FileInputStream(myPath); //5.開始上傳 輸出流對象 byte[] buf = new byte[1024]; while ((bis.read(buf)) != -1){ out.write(buf,0,buf.length); } out.write(newLine.getBytes()); bis.close(); byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine).getBytes(); out.write(end_data); out.flush(); out.close(); //6.接受服務(wù)器端返回數(shù)據(jù)-響應(yīng) BufferedReader br = new BufferedReader(new InputStreamReader(collection.getInputStream())); System.out.println(br.readLine()); }
調(diào)用例子:
postFileToServer("http://127.0.0.1/upLoadFile.php","file","test.jpg","image/jpeg","C:\\Users\\a2867\\Desktop\\壁紙\\2.jpg");
請求 "本地服務(wù)器的
upLoadFile.php
文件" 上傳到 "本機(jī)桌面壁紙文件夾里面的2.jpg
梧兼,類型是image/jpeg
的圖片" 到服務(wù)器端中,上傳后的名字是test.jpg
智听。各個參數(shù)意義:
serverURL:請求那個URL即那個后臺文件處理我們當(dāng)前的請求
postKey:上傳文件的Key羽杰,就是upLoad.php中
$file = $_FILES["file"]
中的"file"
fileName:上傳后文件的名稱
fileType:上傳的文件類型
myPath:上傳的文件在電腦上的路徑
參考文章:
HTTP POST請求報文格式分析與Java實現(xiàn)文件上傳