1. 程式人生 > >黑馬程式設計師_網路程式設計TCP之學習筆記

黑馬程式設計師_網路程式設計TCP之學習筆記

------- android培訓java培訓、期待與您交流! ----------

TCP網路傳輸。

客戶端和服務端,分別對應著兩個物件。

Scoket(客戶端)和ServerSocket(服務端)。

  Socket(String  address, int port)

      建立一個流套接字並將其連線到指定 IP 地址的指定埠號。(服務端)

這個客戶端在一建立的情況下就去連線服務端。

通過指定地址和指定的埠找到服務端。並與之建立連線。

因為TCP是面向連線的。所以在建立Socket服務時候,就要有服務端存在,並連線成功,形成通路後,在該通路進行資料的傳輸。

步驟:
1.建立Socket服務,並指定要連線的主機和埠。

   Socket s = newSocket("localhost",10002);

   如果上述這步成功了的話。也就是通路建立了。

當通路一建立,就有一個Socket流,也就是網路流。

兩端之間建立了一個網路流。Socket中既有輸入流也有輸出流,

一旦建立連線,在Socket中就可以取到輸入流和輸出流對資料進行操作。

流不用去建立,只要通路一建立,流就存在了。

getOutputStream()

         返回此套接字的輸出流。

getInputStream()

         返回此套接字的輸入流。

TCP網路通訊原理圖:


服務端

服務端沒有自己的流物件。

服務端和客戶端連線的時候,就使用了客戶端的流物件和客戶端進行操作。

例子:

import java.net.Socket;
import java.io.*;
import java.net.*;

public class TCPDemo
{
    public static void main(String[] args) throws Exception
    {
	Socket s = new Socket("localhost",10003);
	//給服務端傳送一個文字資料,現獲取Socket流中的輸出流
	OutputStream os = s.getOutputStream();
	
	os.write("大斌".getBytes());
	
	s.close();
	
	
    }
}
class ServerDemo
{
   public static void main(String[] args) throws Exception
   {
      //1.建立服務端的Socket服務,ServerSocket();並監聽一個埠
      ServerSocket ss = new ServerSocket(10003); 
      //2.獲取連線過來的客戶端物件.通過ServerSocket的accept方法
      //所以這個方法是阻塞式的方法,沒有連線就會等
      Socket s = ss.accept();
      //3.客戶端要是發過來資料,服務端要使用對應的客戶端物件,並獲取
      //到該客戶端物件的讀取流,來讀取發過來的資料,並列印在控制檯。
      InputStream is = s.getInputStream();
      byte[] bytes = new byte[1024];
      int len = is.read(bytes);
      System.out.println(new String(bytes,0,len));
      System.out.println(s.getInetAddress()); 
      //4.關閉服務端。
   } 
}


在TCP中,必須先啟動服務端。然後再使用客戶端。

演示tcp傳輸的客戶端和服務端的互訪。

需求:客戶端給服務端傳送資料,服務端收到後,給客戶端反饋資訊。

客戶端:步驟

 1.建立socket服務。指定要連線的主機和埠

 2.獲取socket流中的輸出流,將資料寫到該流中。通過網路傳送給服務端。

 3.獲取socket流中的輸入流,將伺服器反饋的資料獲取到,並列印。

 4.關閉客戶端資源。

 import java.io.*;
import java.net.*;
public class TCPTest
{
    public static void main(String[] args) throws Exception
    {
	Socket socket = new Socket("localhost",10010);
	OutputStream os = socket.getOutputStream();
	BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
	bw.write("123123");
	bw.newLine();
	bw.flush();
	InputStream is  = socket.getInputStream();
	byte[] bytes = new byte[1024];
	int len = is.read(bytes);//阻塞式方法。只有在服務端傳送完成資料之後
	//才解除了阻塞
	System.out.println(new String(bytes,0,len));
	socket.close();
    }
}
class Receive
{
    public static void main(String[] args) throws Exception
    {
	ServerSocket ss = new ServerSocket(10010);
	Socket s = ss.accept();//阻塞式方法
	InputStream is = s.getInputStream();
	BufferedReader bis = new BufferedReader(new InputStreamReader(is));
	String reviceString = bis.readLine();
	System.out.println(reviceString);
	OutputStream os  = s.getOutputStream();
	os.write("我收到你的資料了".getBytes());
	s.close();
	ss.close();	
    }
}

TCP練習:建立一個文字轉換伺服器。

客戶端給服務端傳送文字,服務端會將文字轉成大寫在返回給客戶端。

分析:

客戶端:既然是操作裝置上的資料,麼就可以使用io技術,並按照io的操作規律來思考。

源:鍵盤錄入,目的:網路輸出流,而且操作的是文字資料。可以選擇字元流。

public class TCPTest//客戶端
{
    public static void main(String[] args) throws Exception
    {
	Socket s  = new Socket("localhost",10010);
	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//源
	String line = null;
	BufferedWriter bw  = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));//目的
	BufferedReader brNet = new BufferedReader(new InputStreamReader(s.getInputStream()));//源
	while((line = br.readLine())!= null)
	{
	    bw.write(line);
	    bw.newLine();
	    bw.flush();   
	    String stringNet = brNet.readLine();
	    System.out.println(stringNet);
	    
	}
br.close();
	s.close();//當關閉此網路流的時候,加了一個結束標記。告訴服務端我沒有事情了。
    }
}


服務端:

源:Socket讀取流

目的:Socket輸出流

都是文字。

class Receive
{
    public static void main(String[] args) throws Exception
    {
	ServerSocket ss = new ServerSocket(10010);
	Socket s = ss.accept();
	System.out.println("成功了");
	BufferedReader bis = new BufferedReader(new InputStreamReader(s.getInputStream()));
	String line = null;
	BufferedWriter bw  = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
	while((line = bis.readLine())!=null)
	{
	    System.out.println(line);
	
	  line = line.toUpperCase();
	  bw.write(line);
	  bw.newLine();
	  bw.flush();
	}
	ss.close();
	s.close();
    }
}

一個檔案上傳的案例:

public class CopyPicture
{
    public static void main(String[] args) throws Exception
    {
	Socket s = new Socket("localhost",10010);
	BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.gif"));
	byte[] bytes = new byte[1024];
	int len = 0;
	BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
	while((len = bis.read(bytes))!=-1)
	{
	      bos.write(bytes,0,len);
	      bos.flush();
	}
	s.shutdownOutput();
	InputStream isNet = s.getInputStream();
	byte[] bytes1 = new byte[1024];
	int len1 = isNet.read(bytes1);
	System.out.println(new String(bytes1,0,len1));
	bis.close();
	s.close();
    }
}
class RecevicePicture
{
    public static void main(String[] args) throws Exception
    {
	ServerSocket ss = new ServerSocket(10010);
	Socket s  = ss.accept();
	InputStream fis = s.getInputStream();
	FileOutputStream fos = new FileOutputStream("copy.gif");
	OutputStream os = s.getOutputStream();
	byte[] bytes = new byte[1024];
	int len = 0;
	while((len = fis.read(bytes))!=-1)
	{
	    fos.write(bytes,0,len);
	}
	os.write("長傳成功".getBytes());
	fos.close();
	s.close();
	ss.close();	
    }
}


下邊,我們使用更高階的協議去完成一個自定義瀏覽器和tomcat伺服器的處理請求。

可以直接使用應用層的協議去封裝。(底層封裝了傳輸層協議,更加的方便)

URL url = newURL("http://localhost:8080/GP2/index.jsp");

//首先將url地址封裝到一個物件為URL中。

URLConnectionconn = url.openConnection();

//這個URLConnection就封裝了有關底層協議的一些資訊。

//也就通過一些應用層的協議進行拆包,並將這些屬性封裝到這個物件中去.

//然後通過openConnection()在內部幫助我們去完成Socket等一系列連線動作。

//所以不用寫Socket(是在傳輸層上的協議),而且是帶著協議去封裝的。

//當連線成功,當然可以將其服務端傳遞過來的資料拿到。也就是得到輸入流物件。

例子:

import java.net.*;
import java.io.*;
public class Test
{
    public static void main(String[] args)throws Exception
    {
	URL url = new URL("http://localhost:8080/MyWeb/1.html");	
	System.out.println(url.getProtocol());
	System.out.println(url.getHost());
	//列印上邊構造的URL的一些資訊
	URLConnection uc = url.openConnection();
	//在內部做了連結的動作,並得到了一個URLConnection
	InputStream is = uc.getInputStream();//將Socket中的InputStream封裝好返回
	//得到返回物件的流
	BufferedReader br = new BufferedReader(new InputStreamReader(is));
	String line = null;
	while((line = br.readLine())!=null)
	{
	    System.out.println(line);
	}
    }
}


這時候傳遞過來的資料就沒有了響應頭。而直接打出html中的資訊。

這是因為,當底層的網路協議把資料封裝好之後(包括響應頭),通過應用層協議去拆包之後,將資料和響應頭分離開來,封裝到了URL物件中,URL物件的流得到的資料就只剩網頁上的資料了,而頭資訊被封裝到了URL物件中。(拆包),如下圖所示

而這些響應頭中的資訊,可以通過URLConnection物件中的一系列方法進行簡析.

比如說:System.out.println(conn.getHeaderField("Server"));

可以得到有關伺服器的資訊。

當瀏覽器寫入一個網址之後,去訪問某一臺主機的時候.它到底做什麼了事情.


1.在上網的時候,先去本地找hosts中的各自域名的對映關係.

當本地有,就返回本地的,如localhost,如果本地沒有,2.就去各自運營商提供的域名伺服器DNS中去尋找對映關係,最後使用ip地址找到對應的伺服器,使用埠找到對應的應用程式.如果直接輸入ip地址,則不走域名簡析.