TCP通信需要明確的幾點(diǎn):
tcp通信是面向連接的,需要先啟動(dòng)服務(wù)端,再啟動(dòng)客戶(hù)端。
客戶(hù)端和服務(wù)端都要?jiǎng)?chuàng)建套接字對(duì)象,客戶(hù)端需要指定服務(wù)端套接字(ip+port),而服務(wù)端必須指定服務(wù)端口。
1
2
|
Socket client_socket = new Socket("192.168.100.17",8888); //客戶(hù)端套接字(Socket類(lèi)的套接字為已連接套接字) ServerSocket listen_socket = new ServerSocket(8888); //服務(wù)端套接字,此時(shí)為監(jiān)聽(tīng)套接字(已經(jīng)bind()地址和端口了) |
服務(wù)端需要使用accept()方法將監(jiān)聽(tīng)套接字轉(zhuǎn)變?yōu)橐堰B接套接字。這個(gè)監(jiān)聽(tīng)套接字可以生成多個(gè)已連接套接字,這樣連接后還能監(jiān)聽(tīng)其他客戶(hù)端的請(qǐng)求。因此,這里應(yīng)該使用多線(xiàn)程實(shí)現(xiàn)并發(fā)訪(fǎng)問(wèn)。獲得了已連接套接字,就可以獲取很多客戶(hù)端的信息,例如客戶(hù)端的ip地址,發(fā)送請(qǐng)求的端口等。
1
2
3
|
Socket server_scoket = socket.accept(); Socket server_scoket2 = socket.accept(); Socket server_scoket3 = socket.accept(); |
服務(wù)端要實(shí)現(xiàn)并發(fā)連接,大致使用如下代碼:其中ThreadTask是線(xiàn)程任務(wù)對(duì)象。
1
2
3
4
5
6
7
|
public static void main(String[] args) throws IOException { ServerSocket listen_sock = new ServerSocket(8888); //監(jiān)聽(tīng)套接字只需創(chuàng)建一個(gè),因此在任務(wù)之外 while (true) { //每建立一個(gè)連接,就開(kāi)啟一個(gè)線(xiàn)程 Socket conn_sock = listen_sock.accept(); //沒(méi)有新連接進(jìn)來(lái)時(shí),main主線(xiàn)程阻塞在此 new Thread(new ThreadTask(conn_sock)).start(); } } |
客戶(hù)端需要根據(jù)已連接套接字獲取輸出流,服務(wù)端需要根據(jù)套接字獲取輸入流。當(dāng)然,既然有了已連接套接字,那么獲取無(wú)論哪一端都可以獲取到輸入流、輸出流。
1
2
|
OutputStream send_stream = client_socket.getOutputStream(); //客戶(hù)端獲取輸出流 InputStream recv_stream = server_socket.getInputStream(); |
服務(wù)端應(yīng)主動(dòng)關(guān)閉已連接套接字,至于監(jiān)聽(tīng)套接字則在合適的地方關(guān)閉。
服務(wù)端應(yīng)該循環(huán)不斷地負(fù)責(zé)接收。
簡(jiǎn)單的Client端:
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
|
import java.io.IOException; import java.io.OutputStream; import java.net.Socket; public class TCPClient { public static void main(String[] args) { // 1.創(chuàng)建客戶(hù)端套接字 Socket c_sock = null; OutputStream client_outstream = null; try { c_sock = new Socket("192.168.0.124",8888); // 2.獲取輸出流 client_outstream = c_sock.getOutputStream(); // 3.輸出數(shù)據(jù) client_outstream.write("Hello,i'm coming".getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { if(c_sock != null){ try{ c_sock.close(); } catch(IOException e) { e.printStackTrace(); } } } } } |
簡(jiǎn)單的Server端:
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
|
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) { // 1.創(chuàng)建監(jiān)聽(tīng)套接字 ServerSocket listen_sock = null; try { listen_sock = new ServerSocket(8888); } catch(IOException i) { i.printStackTrace(); } Socket server_sock = null; InputStream in_sock = null; while (true) { try { // 2.和客戶(hù)端建立連接,生成已連接套接字,并獲取客戶(hù)端ip地址 server_sock = listen_sock.accept(); String client_ip = server_sock.getInetAddress().getHostAddress(); System.out.println("Client: " + client_ip + " connected"); // 3.根據(jù)已連接套接字,獲取輸入流,讀取客戶(hù)端發(fā)送的數(shù)據(jù) in_sock = server_sock.getInputStream(); BufferedReader bufr = new BufferedReader(new InputStreamReader(in_sock)); String line = null; while ((line = bufr.readLine()) != null) { System.out.println(line); } // 4.關(guān)閉已連接套接字 server_sock.close(); } catch (IOException e) { e.printStackTrace(); } } } } |
以下是tcp實(shí)現(xiàn)文件上傳功能:
客戶(hù)端除了套接字的輸出流,還有讀取本地文件的輸入流,還有套接字的輸入流來(lái)讀取來(lái)自服務(wù)端的反饋信息。
服務(wù)端也同樣有三流:套接字的輸入、輸出流,寫(xiě)入上傳目標(biāo)文件的輸出流。
客戶(hù)端讀取本地文件的所有數(shù)據(jù)后,需要使用套接字的shutdownOutput()來(lái)通知服務(wù)端套接字的輸出流已到末尾。
服務(wù)端為了能為多人提供上傳功能,需要使用多線(xiàn)程實(shí)現(xiàn)并發(fā)連接。
Client端:
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
|
import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.Socket; public class UploadClient { public static void main(String[] args) { // TODO Auto-generated method stub String server_addr = "192.168.0.124"; int server_port = 8888; Socket send_sock = null; FileInputStream local_read = null; try { // 1.客戶(hù)端套接字 send_sock = new Socket(server_addr, server_port); // 2.獲取連接管道的輸出流 OutputStream send_stream = send_sock.getOutputStream(); // 3.字節(jié)輸入流讀取本地文件數(shù)據(jù),并使用套接字的輸出流發(fā)送出去 local_read = new FileInputStream("d:/myjava/net/SQL.docx"); byte[] buf = new byte[1024]; int len = 0; while ((len = local_read.read(buf)) != -1) { send_stream.write(buf, 0, len); } // 4.標(biāo)記輸出流到結(jié)尾 send_sock.shutdownOutput(); // 5.接收服務(wù)端的反饋數(shù)據(jù),如上傳成功,上傳失敗等 InputStream recv_stream = send_sock.getInputStream(); BufferedReader ack_recv = new BufferedReader(new InputStreamReader(recv_stream)); String line = null; while ((line = ack_recv.readLine()) != null) { System.out.println(line); } } catch (IOException i) { i.printStackTrace(); } finally { if (send_sock != null) { try { send_sock.close(); local_read.close(); } catch (IOException i1) { i1.printStackTrace(); } } if (local_read != null) { try { local_read.close(); } catch (IOException i2) { i2.printStackTrace(); } } } } } |
Server端:
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
|
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class UploadServer { public static void main(String[] args) throws IOException { ServerSocket listen_sock = new ServerSocket(8888); //監(jiān)聽(tīng)套接字只需創(chuàng)建一個(gè),因此在任務(wù)之外 while (true) { //每建立一個(gè)連接,就開(kāi)啟一個(gè)線(xiàn)程 Socket conn_sock = listen_sock.accept(); //沒(méi)有新連接進(jìn)來(lái)時(shí),main主線(xiàn)程阻塞在此 new Thread(new Uploader(conn_sock)).start(); } } } class Uploader implements Runnable { private File dest_dir = new File("d:/temp"); // 上傳目錄 private Socket conn_sock = null; // 連接套接字 InputStream recv_stream = null; FileOutputStream dest_stream = null; Uploader(Socket conn_sock) throws IOException { this.conn_sock = conn_sock; } public void run() { try { if (!dest_dir.exists()) { dest_dir.mkdirs(); } // 1.獲取連接管道的輸入流 recv_stream = conn_sock.getInputStream(); // 客戶(hù)端ip String client_ip = conn_sock.getInetAddress().getHostAddress(); System.out.println(client_ip + ".....connected"); // 2.文件的上傳位置,即輸出目標(biāo),以ip命名。如果文件已存在,則使用括號(hào)加數(shù)字新建文件,如"192.168.100.23(1).txt" File dest_file = new File(dest_dir, client_ip + ".docx"); int count = 1; while (dest_file.exists()) { dest_file = new File(dest_dir, client_ip + "(" + count + ")" + ".docx"); count++; } // 3.讀取數(shù)據(jù)并寫(xiě)入目標(biāo)文件 dest_stream = new FileOutputStream(dest_file); byte[] buf = new byte[1024]; int len = 0; while ((len = recv_stream.read(buf)) != -1) { dest_stream.write(buf, 0, len); } // 4. 向客戶(hù)端反饋信息 OutputStream ack_send = conn_sock.getOutputStream(); byte[] text = "upload successful!".getBytes(); ack_send.write(text); } catch (IOException e1) { e1.printStackTrace(); } finally { if (dest_stream != null) { try { dest_stream.close(); } catch (IOException i) { i.printStackTrace(); } } if (conn_sock != null) { try { conn_sock.close(); } catch (IOException i) { i.printStackTrace(); } } } } } |
以上這篇java 網(wǎng)絡(luò)編程之TCP通信和簡(jiǎn)單的文件上傳功能實(shí)例就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。
原文鏈接:http://www.cnblogs.com/f-ck-need-u/p/8250952.html