黑馬程式設計師_網路程式設計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地址,則不走域名簡析.