Java的面向?qū)ο缶幊?/a>">Java的面向?qū)ο缶幊?/a>
673
2022-05-29
☆ TCP
建立連接,形成傳輸數(shù)據(jù)的通道。
在連接中進行大數(shù)據(jù)量傳輸
通過三次握手完成連接,是可靠協(xié)議
必須建立連接,效率會稍低
Socket 和
ServerSocket類
TCP傳輸
TCP Socket:IP地址和端口,套接字
Socket和ServerSocket
建立客戶端和服務器端
建立連接后,通過Socket中的IO流進行數(shù)據(jù)的傳輸
關(guān)閉socket
同樣,客戶端與服務器端是兩個獨立的應用程序。
TCP傳輸編程
☆基本思路(客戶端)
客戶端需要明確服務器的ip地址以及端口,這樣才可以去試著建立連接,如果連接失敗,會出現(xiàn)異常。
連接成功,說明客戶端與服務端建立了通道,那么通過IO流就可以進行數(shù)據(jù)的傳輸,而Socket對象已經(jīng)提供了輸入流和輸出流對象,通過getInputStream(), getOutputStream()獲取即可。
與服務端通訊結(jié)束后,關(guān)閉Socket。
☆基本思路(服務器端)
服務端需要明確它要處理的數(shù)據(jù)是從哪個端口進入的。
當有客戶端訪問時,要明確是哪個客戶端,可通過accept()獲取已連接的客戶端對象,并通過該對象與客戶端通過IO流進行數(shù)據(jù)傳輸。
當該客戶端訪問結(jié)束,關(guān)閉該客戶端。
基于TCP的Socket通信流程
TCP傳輸編程代碼:
☆客戶端
通過Socket建立對象并指定要連接的服務端主機以及端口。
Socket s = new Socket(“192.168.1.1”,9999); OutputStream out = s.getOutputStream(); out.write(“hello”.getBytes()); s.close();
1
2
3
4
☆服務器端
建立服務端需要監(jiān)聽一個端口
ServerSocket ss = new ServerSocket(9999); Socket s = ss.accept (); InputStream in = s.getInputStream(); byte[] buf = new byte[1024]; int num = in.read(buf); String str = new String(buf,0,num); System.out.println(s.getInetAddress().toString()+”:”+str); s.close(); ss.close();
1
2
3
4
5
6
7
8
9
最簡單的TCP演示實例:
客戶端代碼:
package cn.hncu.tcp; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; /** * 客戶端 * @author 陳浩翔 * * 2016-5-10 */ public class MyClientSocket { public static void main(String[] args) { try { //因為是在自己本機上演示,IP就直接填寫本機10.30.7.95的了。 //這個端口和IP都是服務器端的(自己可以改的) Socket s = new Socket("10.30.7.95", 9999); //和服務器進行三次握手,若失敗則出異常,否則返回和對方通訊的socket OutputStream os = s.getOutputStream(); //發(fā)送數(shù)據(jù) os.write("你好,服務器!".getBytes()); //接收服務器端的反饋 InputStream in = s.getInputStream(); DataInputStream din = new DataInputStream(in); System.out.println(din.readUTF()); s.close(); din.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
服務器端代碼:
package cn.hncu.tcp; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * 服務器端 * @author 陳浩翔 * * 2016-5-10 */ public class MyServerSocket { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(9999); while(true){ System.out.println("準備接收一個數(shù)據(jù)..."); Socket s = server.accept();//阻塞式方法 System.out.println("接收了一個數(shù)據(jù)..."); //讀--從客戶端讀數(shù)據(jù) InputStream in = s.getInputStream(); byte buf[] = new byte[1024]; in.read(buf); System.out.println("read info: "+new String(buf)); //寫--應答客戶端--向他寫數(shù)據(jù) OutputStream out = s.getOutputStream(); DataOutputStream dout = new DataOutputStream(out); dout.writeUTF("你好,"+s.getInetAddress().getHostAddress()+" ,你的信息已收到。"); dout.close(); s.close(); } } catch (IOException e) { e.printStackTrace(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
客戶端運行結(jié)果:
你好,10.30.7.95 ,你的信息已收到。
1
2
服務器端運行結(jié)果:
☆思考
對于Web服務器而言,當有多個客戶端同時訪問服務器時,服務端又如何提供服務呢?
1
☆TCP傳輸最容易出現(xiàn)的問題
客戶端連接上服務端,兩端都在等待,沒有任何數(shù)據(jù)傳輸。
通過例程分析:
因為read方法或者readLine方法是阻塞式。
解決辦法:
自定義結(jié)束標記(必須定義傳輸文件中沒有這個這個字符串的,不然會出現(xiàn)接收數(shù)據(jù)不完整)
使用shutdownInput,shutdownOutput方法。
編程練習
☆上傳文本文件
讀取一個本地文本文件,將數(shù)據(jù)發(fā)送到服務端,服務器端對數(shù)據(jù)進行存儲。 存儲完畢后,給客戶端一個提示。
文本文件發(fā)送的客戶端
package cn.hncu.tcp.upload; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; /** * 文本文件發(fā)送的客戶端 * @author 陳浩翔 * * 2016-5-10 */ public class UploadTextClient { public static void main(String[] args) { try { Socket s = new Socket("", 10006); //思路:把本地文件的數(shù)據(jù)讀取出來通過 s.getOutputStream()獲得的out對象發(fā)送出去 BufferedReader bf = new BufferedReader(new FileReader("tempfiles\client.txt")); OutputStream out = s.getOutputStream();//這里的輸出流 對應的是服務器端的輸入流 PrintWriter pw = new PrintWriter(out,true);//建議不要用BufferedWriter //!!!!!!!!!!!!!!!!!這個true不要忘了!---自動刷新 //現(xiàn)在大家寫網(wǎng)絡(luò)傳輸文件,一般是用PrintWriter String str=null; while((str=bf.readLine())!=null){ pw.println(str); } //給服務器發(fā)送結(jié)束標記---上傳結(jié)束,要加結(jié)束標記, //否則服務器在數(shù)據(jù)接收完畢時再調(diào)用read()或readLine()時會出異常 //法1:pw.println("over#$@#@$");//不能出現(xiàn)文件中存在的結(jié)束關(guān)鍵字---搞特殊一點 //法2---建議采用該種方式---由socket內(nèi)部來指定結(jié)束標記 s.shutdownOutput(); bf.close(); //接收服務器端反饋 InputStream in = s.getInputStream(); DataInputStream din = new DataInputStream(in); System.out.println("server應答:"+din.readUTF()); s.close(); din.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
文本文件接收的服務器端
package cn.hncu.tcp.upload; import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; /** * 文本文件接收的服務器端 * @author 陳浩翔 * * 2016-5-10 */ public class UploadTextServer { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(10006); Socket s = server.accept(); System.out.println(s.getInetAddress().getHostAddress()+"...發(fā)送消息來"); //讀取客戶端上傳過來的文本文件 //源 ---socket(字節(jié)流)---額外:需要轉(zhuǎn)換成字符流 ,緩存流 InputStream in = s.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); //目的 ---硬盤字符流 FileWriter---額外:打印流 PrintWriter pw = new PrintWriter(new FileWriter("tempfiles\server.txt"),true); String line = null; while((line=br.readLine())!=null){ // if("over#$@#@$".equals(line)){//自己定義的結(jié)束標志 // break; // } pw.println(line); } pw.close(); //上傳成功,給客戶端一個提示信息 OutputStream out = s.getOutputStream(); DataOutputStream dout = new DataOutputStream(out); dout.writeUTF("文件上傳成功!"); s.close(); server.close(); dout.close(); } catch (IOException e) { e.printStackTrace(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
測試通過!
☆上傳圖片文件
客戶端需求:把一個圖片文件發(fā)送到服務端并讀取回饋信息。要求判斷文件是否存在及格式是否為jpg或gif并要求文件小于2M。
服務端需求:接收客戶端發(fā)送過來的圖片數(shù)據(jù)。進行存儲后,回饋一個 上傳成功字樣。支持多用戶的并發(fā)訪問。
圖片文件的發(fā)送-客戶端
package cn.hncu.tcp.upload; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; import java.util.Scanner; import javax.swing.JOptionPane; /** * 圖片文件的發(fā)送-客戶端 * @author 陳浩翔 * * 2016-5-10 */ public class UploadPicClient { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("請輸入圖片路徑:"); String str = sc.next(); File file = new File(str); if(!(file.exists()&& file.isFile())){ JOptionPane.showMessageDialog(null, "文件不存在!"); return ; } if(!(file.getName().endsWith(".jpg")||file.getName().endsWith(".gif"))){ JOptionPane.showMessageDialog(null, "文件格式不對,文件擴展名必須是jpg或gif!"); return ; } if( file.length()>=1024*1024*2){ JOptionPane.showMessageDialog(null, "文件過大,不應超過2M,請重新上傳!"); return; } //上傳 try { Socket s = new Socket("10.30.7.95", 10007); BufferedInputStream bin = new BufferedInputStream(new FileInputStream(str)); OutputStream out = s.getOutputStream(); byte buf[] = new byte[1024]; int len=0; while((len=bin.read(buf))!=-1){ out.write(buf, 0, len); } s.shutdownOutput();//告訴服務器,文件上傳完畢 //讀取服務器的回饋信息 InputStream in = s.getInputStream(); byte buf2[] = new byte[1024]; int len2 = in.read(buf2); System.out.println(new String(buf2, 0, len2)); //關(guān)流 out.close(); bin.close(); s.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
圖片文件接收-服務器端
package cn.hncu.tcp.upload; import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * 圖片文件接收-服務器端 * @author 陳浩翔 * * 2016-5-10 */ public class UploadPicServer { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(10007); while(true){ Socket s = server.accept();//阻塞方法 //只負責和客戶端進行握手 new Thread(new UploadThread(s) ).start(); } } catch (IOException e) { e.printStackTrace(); } } } class UploadThread implements Runnable{ private Socket s; public UploadThread(Socket s) { this.s = s; } @Override public void run() { String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+"...發(fā)來圖片"); try { BufferedInputStream bin = new BufferedInputStream(s.getInputStream()); File dir = new File("g:\mypic"); if(!dir.exists()){ dir.mkdir();//文件夾不存在,創(chuàng)建mypic文件夾 } int count=1; //我覺得這里的后綴名,需要通過發(fā)送方也發(fā)過來的 File file = new File(dir, ip+".jpg"); while(file.exists()){ file = new File(dir,ip+"("+(count++) +")"+".jpg"); //帶號的文件名 } FileOutputStream fout = new FileOutputStream(file); //從socket流中讀取數(shù)據(jù),存儲到本地文件。相當于對拷 byte buf[] = new byte[1024]; int len=0; while( (len=bin.read(buf))!=-1){ fout.write(buf, 0, len); } //圖片接收完畢 //向客戶端發(fā)送回饋信息 OutputStream out = s.getOutputStream(); out.write( "上傳成功".getBytes() ); fout.close(); s.close(); } catch (IOException e) { e.printStackTrace(); } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
有一個小bug。是服務端接收的,因為我把后綴名統(tǒng)一為jpg了,gif的圖片可以上傳,只是變成靜圖了(jpg)。可以通過改后綴名,再把這圖片改回去動圖。還有一種方法,在上傳的時候,把后綴名也上傳,再通過服務器解析就可以解決這個問題。
因為時間問題,我就不寫了,這個很簡單的。
只是多發(fā)送了一個后綴名過去而已。
我還是把那個后綴名的解決辦法寫了下:
在客戶端:修改的代碼如下:
//上傳 try { Socket s = new Socket("10.30.7.95", 10007); BufferedInputStream bin = new BufferedInputStream(new FileInputStream(str)); //上傳文件后綴###########增加的 OutputStream out = s.getOutputStream(); String fileName = file.getName(); out.write(fileName.substring(fileName.length()-4, fileName.length()).getBytes()); byte buf[] = new byte[1024]; int len=0; while((len=bin.read(buf))!=-1){ out.write(buf, 0, len); } s.shutdownOutput();//告訴服務器,文件上傳完畢 //讀取服務器的回饋信息 InputStream in = s.getInputStream(); byte buf2[] = new byte[1024]; int len2 = in.read(buf2); System.out.println(new String(buf2, 0, len2)); //關(guān)流 out.close(); bin.close(); s.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
服務器端:
@Override public void run() { String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+"...發(fā)來圖片"); try { BufferedInputStream bin = new BufferedInputStream(s.getInputStream()); File dir = new File("g:\\mypic"); if(!dir.exists()){ dir.mkdir();//文件夾不存在,創(chuàng)建mypic文件夾 } //讀取上傳過來的圖片后綴是什么!######### char cbuf[] = new char[4]; InputStreamReader insr = new InputStreamReader(bin); insr.read(cbuf); String str = new String(cbuf); int count=1; //我覺得這里的后綴名,需要通過發(fā)送方也發(fā)過來的 File file = new File(dir, ip+str); while(file.exists()){ file = new File(dir,ip+"("+(count++) +")"+str); //帶號的文件名 } FileOutputStream fout = new FileOutputStream(file); //從socket流中讀取數(shù)據(jù),存儲到本地文件。相當于對拷 byte buf[] = new byte[1024]; int len=0; //#########必須有這一句 bin.read(buf, 0, 8); while( (len=bin.read(buf))!=-1){ fout.write(buf, 0, len); } //圖片接收完畢 //向客戶端發(fā)送回饋信息 OutputStream out = s.getOutputStream(); out.write( "上傳成功".getBytes() ); fout.close(); s.close(); } catch (IOException e) { e.printStackTrace(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
好了,基本上就是這樣的,如果想要更好,做個圖形界面就好看了。自己動下手吧~~
Java TCP/IP 網(wǎng)絡(luò)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。