網路編程基礎
基礎知識
客戶端:指瀏覽器或者自定義的客戶端。
服務端:像Tomcat伺服器或者自定義客戶端。
TCP/IP
TCP:傳輸層協議。
IP:網路層協議。
TCP/UDP
TCP與UDP區別
TCP使用案例:用於RPC介面調用,發送電子郵件等需要可靠性傳輸的事情。
UDP使用案例:用於影片這類的傳輸。
相同點:
- TCP與UDP都是網路層的通訊協議,只是通訊的協議不同。
不同點:
- TCP協議在通訊前要先通過三次握手的方式建立連接,保證通訊的可靠性。而UDP不需要建立連接接可以發送數據包,因此接收方不一定能收到,因此是不可靠的。
- TCP協議需要建立連接和釋放連接,因此傳輸效率低。而UDP不需要釋放資源,因此開銷小,速度快。
- UDP可以進行廣播發送,而TCP是點對點通訊。
- TCP在建立連接後可以進行大數據量的傳輸,而UDP會限制每個數據包的傳輸大小在64K以內。
TCP
三次握手
客戶端建立連接請求時的三次握手:
第一次握手:客戶端向服務端發送syn報文和序號x;
第二次握手:服務端接收到客戶端的請求,發送ack=x+1報文,並發送序號y,syn報文。
第三次握手:客戶端接收到服務端的請求,發送ack= y+1報文,並發送序號z。
為什麼是三次握手而不是二次或者四次五次呢?
因此根據三次握手我們客戶端和服務端都可以知道自己發送正常,對方接收正常;而二次握手的話,服務端並不知道自己發送正常,只能知道客戶端發送接收正常,自己接收正常,而不知道自己發送正常。而三次握手剛好就滿足了這個條件。既然三次握手就滿足了,四次五次就會顯得多餘了,雖然更多次的握手可以更加保證通訊的正常,但是正常來說三次握手就能保證通訊99%是可靠的,再多次的握手可靠性的提高並不高,意義不大。
四次揮手
客戶端服務端都可以通過四次揮手關閉連接,但一般都是客戶端發起四次揮手請求來關閉連接,因為我們服務端一般是24小時在線服務的。
以客戶端發起斷開連接為例:
客戶端告訴服務端我要斷開連接了。
服務端相應客戶端說我收到你的斷開連接請求了。
服務端斷開連接,並發送請求告訴客戶端我和你斷開連接了。
客戶端收到斷開連接請求斷開了連接,並發送確認斷開的請求告訴服務我和你斷開連接了,這時候服務端就接收不到客戶端的請求了,如果接收到了就表示沒有真正斷開連接。
示例小結
public class TCPTest {
/**
* 客戶端
*/
@Test
public void client() throws IOException {
Socket socket = null;
OutputStream os = null;
try {
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
//1創建Socke連接對象,指明要連接的服務端的ip和埠號
socket = new Socket(inetAddress, 9000);
//2獲取輸出流,用於輸出數據
os = socket.getOutputStream();
//3寫數據
os.write("我叫客戶端,你好啊".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4關閉流資源
if (os != null) {
os.close();
}
if (socket != null) {
socket.close();
}
}
}
@Test
public void server() throws IOException {
InputStream is = null;
ByteArrayOutputStream baos = null;
ServerSocket serverSocket = null;
Socket socket = null;
try {
//創建服務端的ServerSocket,指明自己的埠號,給客戶端通過指定的ip和該埠號確定進程進行連接
serverSocket = new ServerSocket(9000);
//accept()用於接收來自客戶端的連接
socket = serverSocket.accept();
//獲取輸入流
is = socket.getInputStream();
//讀取輸入流中的數據,ByteArrayOutputStream該流維護動態數組保存寫入的數據
baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int len;
while ((len = is.read(buff)) != -1) {
baos.write(buff, 0, len);
}
System.out.println(baos.toString());
System.out.println("收到客戶端請求了!");
} catch (IOException e) {
e.printStackTrace();
} finally {
//關閉資源
baos.close();
is.close();
socket.close();
serverSocket.close();
}
}
/**
* 我叫客戶端,你好啊
* 收到客戶端請求了!
*/
}
示例小結——TCP發送接收文件
public class TCPFileTest {
/**
* 客戶端給服務端發生文件
*/
@Test
public void client() throws IOException {
Socket socket = null;
OutputStream os = null;
FileInputStream fis = null;
ByteArrayOutputStream baos = null;
try {
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
//1創建Socke連接對象,指明要連接的服務端的ip和埠號
socket = new Socket(inetAddress, 9000);
//2獲取輸出流,用於輸出數據
os = socket.getOutputStream();
fis = new FileInputStream("是大臣.jpg");
//3寫數據
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
System.out.println("向服務端發送圖片成功!");
//關閉數據的輸出,不然服務端會一直阻塞著接收不往下走
socket.shutdownOutput();
//接收服務端的應答
InputStream is = socket.getInputStream();
baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int length;
while ((length = is.read(buff)) != -1) {
baos.write(buff, 0, length);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4關閉流資源
if (os != null) {
os.close();
}
if (socket != null) {
socket.close();
}
}
}
/**
* 向服務端發送圖片成功!
* 客戶端,你的文件收到了
*/
/**
* 服務端接收文件保存到本地
*
* @throws IOException
*/
@Test
public void server() throws IOException {
InputStream is = null;
FileOutputStream fos = null;
ServerSocket serverSocket = null;
Socket socket = null;
OutputStream os = null;
try {
//創建服務端的ServerSocket,指明自己的埠號,給客戶端通過指定的ip和該埠號確定進程進行連接
serverSocket = new ServerSocket(9000);
//accept()用於接收來自客戶端的連接
socket = serverSocket.accept();
//獲取輸入流
is = socket.getInputStream();
//打開輸出流寫文件
fos = new FileOutputStream("是大臣-copy5.jpg");
byte[] buff = new byte[1024];
int len;
while ((len = is.read(buff)) != -1) {
fos.write(buff, 0, len);
}
System.out.println("收到客戶端請求了!");
//給客戶端寫出應答
os = socket.getOutputStream();
os.write("客戶端,你的文件收到了".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//關閉資源
fos.close();
is.close();
socket.close();
serverSocket.close();
os.close();
}
}
/**
* 收到客戶端請求了!
*/
}
UDP
示例小結
public class UDPTest {
/**
* 發生端
*/
@Test
public void sender() throws IOException {
//1創建socket,發送端不需要指定要發生的ip和埠,直接在要發生的包里指定接收的地址埠即可。
DatagramSocket socket = new DatagramSocket();
byte[] data = "我是發送端,這是給你的一封信!".getBytes();
//數據包要發送的地址
InetAddress address = InetAddress.getByName("127.0.0.1");
//2創建發送包
DatagramPacket packet = new DatagramPacket(data, 0, data.length, address, 9999);
//3發送
socket.send(packet);
System.out.println("發送數據成功!");
//4關閉流
socket.close();
}
/**
* 接收端
*/
@Test
public void receiver() throws IOException {
//1創建接收端socket,要指定接收端的埠號,才能接收
DatagramSocket socket = new DatagramSocket(9999);
//2創建用於接收數據流的數組容器
byte[] buffer = new byte[1024];
//2創建接收包,用於接收數據包
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
//3發送
socket.receive(packet);
//4列印接收到的數據
System.out.println(new String(packet.getData(),0,packet.getLength()));
System.out.println("接收數據成功!");
//5關閉流
socket.close();
}
}
URL
示例小結
public class URLTest {
/**
* 使用url去下載網路文件
*
* @throws IOException
*/
@Test
public void urlTest() throws IOException {
//URL相當於種子
URL url = new URL("//pics4.baidu.com/feed/d009b3de9c82d1586f9efd2871ac9ad1bd3e42bf.jpeg?token=dd219671927c0cb92aebbd8808110a70");
//根據不同的協議強轉不同的urlConnection,HttpsURLConnection或者HttpURLConnection
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
InputStream is = urlConnection.getInputStream();
//寫到本地
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("pic.jpeg"));
byte[] buff = new byte[1024];
int len;
while ((len = is.read(buff)) != -1) {
bos.write(buff, 0, len);
}
System.out.println("下載成功了!");
//關閉流
is.close();
bos.close();
urlConnection.disconnect();
}
}
參考
618-630