TCP協(xié)議(Socket)

TCP協(xié)議是面向連接的通信協(xié)議,即在傳輸數(shù)據(jù)前先在發(fā)送端和接收端建立邏輯連接,然后再傳輸數(shù)據(jù),它提供了兩臺(tái)計(jì)算機(jī)之間可靠無(wú)差錯(cuò)的數(shù)據(jù)傳輸。在TCP連接中必須要明確客戶端與服務(wù)器端,由客戶端向服務(wù)端發(fā)出連接請(qǐng)求,每次連接的創(chuàng)建都需要經(jīng)過“三次握手”。第一次握手,客戶端向服務(wù)器端發(fā)出連接請(qǐng)求,等待服務(wù)器確認(rèn),第二次握手,服務(wù)器端向客戶端回送一個(gè)響應(yīng),通知客戶端收到了連接請(qǐng)求,第三次握手,客戶端再次向服務(wù)器端發(fā)送確認(rèn)信息,確認(rèn)連接。整個(gè)交互過程如下圖所示。

由于TCP協(xié)議的面向連接特性,它可以保證傳輸數(shù)據(jù)的安全性,所以是一個(gè)被廣泛采用的協(xié)議,例如在下載文件時(shí),如果數(shù)據(jù)接收不完整,將會(huì)導(dǎo)致文件數(shù)據(jù)丟失而不能被打開,因此,下載文件時(shí)必須采用TCP協(xié)議。

TCP通信同UDP通信一樣,都能實(shí)現(xiàn)兩臺(tái)計(jì)算機(jī)之間的通信,通信的兩端都需要?jiǎng)?chuàng)建socket對(duì)象。

區(qū)別在于,UDP中只有發(fā)送端和接收端,不區(qū)分客戶端與服務(wù)器端,計(jì)算機(jī)之間可以任意地發(fā)送數(shù)據(jù)。

而TCP通信是嚴(yán)格區(qū)分客戶端與服務(wù)器端的,在通信時(shí),必須先由客戶端去連接服務(wù)器端才能實(shí)現(xiàn)通信,服務(wù)器端不可以主動(dòng)連接客戶端,并且服務(wù)器端程序需要事先啟動(dòng),等待客戶端的連接。

在JDK中提供了兩個(gè)類用于實(shí)現(xiàn)TCP程序,一個(gè)是ServerSocket類,用于表示服務(wù)器端,一個(gè)是Socket類,用于表示客戶端。

通信時(shí),首先創(chuàng)建代表服務(wù)器端的ServerSocket對(duì)象,該對(duì)象相當(dāng)于開啟一個(gè)服務(wù),并等待客戶端的連接,然后創(chuàng)建代表客戶端的Socket對(duì)象向服務(wù)器端發(fā)出連接請(qǐng)求,服務(wù)器端響應(yīng)請(qǐng)求,兩者建立連接開始通信。

示例(簡(jiǎn)單的傳輸字符串):

客戶端:

package cn.itcast.demo3;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.Socket;

/*

*? 實(shí)現(xiàn)TCP客戶端,連接到服務(wù)器

*? 和服務(wù)器實(shí)現(xiàn)數(shù)據(jù)交換

*? 實(shí)現(xiàn)TCP客戶端程序的類 java.net.Socket

*?

*? 構(gòu)造方法:

*? ? ? Socket(String host, int port)? 傳遞服務(wù)器IP和端口號(hào)

*? ? ? 注意:構(gòu)造方法只要運(yùn)行,就會(huì)和服務(wù)器進(jìn)行連接,連接失敗,拋出異常

*? ? ?

*? ? OutputStream? getOutputStream() 返回套接字的輸出流

*? ? ? 作用: 將數(shù)據(jù)輸出,輸出到服務(wù)器

*? ? ?

*? ? InputStream getInputStream() 返回套接字的輸入流

*? ? ? 作用: 從服務(wù)器端讀取數(shù)據(jù)

*? ? ?

*? ? 客戶端服務(wù)器數(shù)據(jù)交換,必須使用套接字對(duì)象Socket中的獲取的IO流,自己new流,不行

*/

public class TCPClient {

public static void main(String[] args)throws IOException {

//創(chuàng)建Socket對(duì)象,連接服務(wù)器

Socket socket = new Socket("127.0.0.1", 8888);

//通過客戶端的套接字對(duì)象Socket方法,獲取字節(jié)輸出流,將數(shù)據(jù)寫向服務(wù)器

OutputStream out = socket.getOutputStream();

out.write("服務(wù)器OK".getBytes());

//讀取服務(wù)器發(fā)回的數(shù)據(jù),使用socket套接字對(duì)象中的字節(jié)輸入流

InputStream in = socket.getInputStream();

byte[] data = new byte[1024];

int len = in.read(data);

System.out.println(new String(data,0,len));

socket.close();

}

}

服務(wù)端:

package cn.itcast.demo3;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.ServerSocket;

import java.net.Socket;

/*

*? 實(shí)現(xiàn)TCP服務(wù)器程序

*? 表示服務(wù)器程序的類 java.net.ServerSocket

*? 構(gòu)造方法:

*? ? ServerSocket(int port) 傳遞端口號(hào)

*?

*? 很重要的事情: 必須要獲得客戶端的套接字對(duì)象Socket

*? ? Socket? accept()

*/

public class TCPServer {

public static void main(String[] args) throws IOException{

ServerSocket server = new ServerSocket(8888);

//調(diào)用服務(wù)器套接字對(duì)象中的方法accept() 獲取客戶端套接字對(duì)象

Socket socket = server.accept();

//通過客戶端套接字對(duì)象,socket獲取字節(jié)輸入流,讀取的是客戶端發(fā)送來(lái)的數(shù)據(jù)

InputStream in = socket.getInputStream();

byte[] data = new byte[1024];

int len = in.read(data);

System.out.println(new String(data,0,len));

//服務(wù)器向客戶端回?cái)?shù)據(jù),字節(jié)輸出流,通過客戶端套接字對(duì)象獲取字節(jié)輸出流

OutputStream out = socket.getOutputStream();

out.write("收到,謝謝".getBytes());

socket.close();

server.close();

}

}

示例(上傳圖片):

客戶端:

package cn.itcast.demo4;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.Socket;

/*

*? 實(shí)現(xiàn)TCP圖片上傳客戶端

*? 實(shí)現(xiàn)步驟:

*? ? 1. Socket套接字連接服務(wù)器

*? ? 2. 通過Socket獲取字節(jié)輸出流,寫圖片

*? ? 3. 使用自己的流對(duì)象,讀取圖片數(shù)據(jù)源

*? ? ? ? FileInputStream

*? ? 4. 讀取圖片,使用字節(jié)輸出流,將圖片寫到服務(wù)器

*? ? ? 采用字節(jié)數(shù)組進(jìn)行緩沖

*? ? 5. 通過Socket套接字獲取字節(jié)輸入流

*? ? ? 讀取服務(wù)器發(fā)回來(lái)的上傳成功

*? ? 6. 關(guān)閉資源

*/

public class TCPClient {

public static void main(String[] args) throws IOException{

Socket socket = new Socket("127.0.0.1", 8000);

//獲取字節(jié)輸出流,圖片寫到服務(wù)器

OutputStream out = socket.getOutputStream();

//創(chuàng)建字節(jié)輸入流,讀取本機(jī)上的數(shù)據(jù)源圖片

FileInputStream fis = new FileInputStream("c:\\t.jpg");

//開始讀寫字節(jié)數(shù)組

int len = 0 ;

byte[] bytes = new byte[1024];

while((len = fis.read(bytes))!=-1){

out.write(bytes, 0, len);

}

//給服務(wù)器寫終止序列

socket.shutdownOutput();

//獲取字節(jié)輸入流,讀取服務(wù)器的上傳成功

InputStream in = socket.getInputStream();

len = in.read(bytes);

System.out.println(new String(bytes,0,len));

fis.close();

socket.close();

}

}

服務(wù)端:

package cn.itcast.demo4;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.Random;

/*

*? TCP圖片上傳服務(wù)器

*? 1. ServerSocket套接字對(duì)象,監(jiān)聽端口8000

*? 2. 方法accept()獲取客戶端的連接對(duì)象

*? 3. 客戶端連接對(duì)象獲取字節(jié)輸入流,讀取客戶端發(fā)送圖片

*? 4. 創(chuàng)建File對(duì)象,綁定上傳文件夾

*? ? ? 判斷文件夾存在, 不存,在創(chuàng)建文件夾

*? 5. 創(chuàng)建字節(jié)輸出流,數(shù)據(jù)目的File對(duì)象所在文件夾

*? 6. 字節(jié)流讀取圖片,字節(jié)流將圖片寫入到目的文件夾中

*? 7. 將上傳成功會(huì)寫客戶端

*? 8. 關(guān)閉資源

*? ? ?

*/

public class TCPServer {

public static void main(String[] args) throws IOException{

ServerSocket server = new ServerSocket(8000);

Socket socket = server.accept();

//通過客戶端連接對(duì)象,獲取字節(jié)輸入流,讀取客戶端圖片

InputStream in = socket.getInputStream();

//將目的文件夾封裝到File對(duì)象

File upload = new File("d:\\upload");

if(!upload.exists())

upload.mkdirs();

//防止文件同名被覆蓋,從新定義文件名字

//規(guī)則:? 域名+毫秒值+6位隨機(jī)數(shù)

String filename="itcast"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";

//創(chuàng)建字節(jié)輸出流,將圖片寫入到目的文件夾中? ? ? ? ? ? ? ? ? ? ? ?

FileOutputStream fos = new FileOutputStream(upload+File.separator+filename);

//讀寫字節(jié)數(shù)組

byte[] bytes = new byte[1024];

int len = 0 ;

while((len = in.read(bytes))!=-1){

fos.write(bytes, 0, len);

}

//通過客戶端連接對(duì)象獲取字節(jié)輸出流

//上傳成功寫回客戶端

socket.getOutputStream().write("上傳成功".getBytes());

fos.close();

socket.close();

server.close();

}

}

開啟子線程:

package cn.itcast.demo4;

import java.io.IOException;

import java.net.ServerSocket;

import java.net.Socket;

public class TCPThreadServer {

public static void main(String[] args) throws IOException{

ServerSocket server = new ServerSocket(8000);

while(true){

//獲取到一個(gè)客戶端,必須開啟新線程

Socket socket = server.accept();

new Thread( new Upload(socket) ).start();

}

}

}

子線程具體方法(上傳):

package cn.itcast.demo4;

import java.io.File;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.Random;

public class Upload implements Runnable{

private Socket socket;

public Upload(Socket socket){this.socket=socket;}

public void run() {

try{

//通過客戶端連接對(duì)象,獲取字節(jié)輸入流,讀取客戶端圖片

InputStream in = socket.getInputStream();

//將目的文件夾封裝到File對(duì)象

File upload = new File("d:\\upload");

if(!upload.exists())

upload.mkdirs();

//防止文件同名被覆蓋,從新定義文件名字

//規(guī)則:? 域名+毫秒值+6位隨機(jī)數(shù)

String filename="itcast"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";

//創(chuàng)建字節(jié)輸出流,將圖片寫入到目的文件夾中? ? ? ? ? ? ? ? ? ? ? ?

FileOutputStream fos = new FileOutputStream(upload+File.separator+filename);

//讀寫字節(jié)數(shù)組

byte[] bytes = new byte[1024];

int len = 0 ;

while((len = in.read(bytes))!=-1){

fos.write(bytes, 0, len);

}

//通過客戶端連接對(duì)象獲取字節(jié)輸出流

//上傳成功寫回客戶端

socket.getOutputStream().write("上傳成功".getBytes());

fos.close();

socket.close();

}catch(Exception ex){

}

}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,544評(píng)論 19 139
  • 1 網(wǎng)絡(luò)編程----TCPNo24 【 public class Server { public static...
    征程_Journey閱讀 1,379評(píng)論 0 4
  • 題目 原題鏈接:B. Kefa and Company 題意 kefa有n個(gè)朋友,每個(gè)朋友有對(duì)應(yīng)的財(cái)富值和友情值,...
    ss5smi閱讀 118評(píng)論 0 0
  • 每個(gè)人都喜歡做自己擅長(zhǎng)的事情,這個(gè)沒有錯(cuò),但是想要有更高一層次的發(fā)展就要有全局思維,要補(bǔ)上不擅長(zhǎng)的一面,不在單一的...
    Lzr_2017閱讀 315評(píng)論 0 2

友情鏈接更多精彩內(nèi)容