1.本文不是大講特講UDP和TCP的區別,或者其流程,只是基於此做些小測試
2.UDP分爲[發送端]與[接收端],[發送方]將數據打包發出去後不關心是否被接收
[發送方]須要持有[接收端]的ip地址及端口,[接收端]能夠在相應端口監聽,不然稱爲[丟包]
3.TCP分爲[服務端]與[客戶端],[服務端]提供服務,若是未開啓,[客戶端]訪問將報錯
[客戶端]須要持有[服務端]的ip地址及端口,[接收端]必須在相應端口監聽,不然報錯
項目源碼:Github:https://github.com/toly1994328/SocketDemojava
1.完成兩個終端(計算機、手機)之間的信息數據傳輸
2.java控制檯、java的GUI、Android界面都只是做爲java的一種展示形式,任何一方均可以做爲發送端或接收端
3.場景一:java控制檯與控制檯間的消息傳輸
4.場景二:java控制檯與GUI間的消息傳輸
5.場景三:java控制檯與Android的消息傳輸android
思路git
* 1---經過DatagramSocket建立對象:端口8081(此端口隨意) * 2---使用DatagramPacket對象打包數據 * 3---使用DatagramSocket對象發送數據包(字節數組,發送長度,ip,端口) * 4---關閉DatagramSocket對象
public class UDPSender { public static void main(String[] args) { System.out.println("這是發送端"); try { //1: 經過DatagramSocket對象建立updSocket服務:端口8081(此端口隨意) DatagramSocket datagramSocket = new DatagramSocket(8081); //2: 使用DatagramPacket對象打包數據 byte[] buf = "土豆土豆,我是地瓜".getBytes(); DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.56.1"), 8080); //3:使用DatagramSocket對象發送數據包(字節數組,發送長度,ip,端口) datagramSocket.send(dp); //4: 關閉DatagramSocket對象 datagramSocket.close(); } catch (Exception e) { e.printStackTrace(); } } }
思路github
1---定義udpSocket服務。一般會監聽一個端口。(給網絡應用定義數字標識--便於該應用程序處理傳來數據過來) 2---定義一個數據包,由於要存儲接收到的字節數據(數據包對象中有更多功能能夠提取字節數據中的不一樣數據信息) 3---經過socket服務的receive方法將收到的數據存入已定義好的數據包中。 4---經過數據包對象的特有功能。將這些不一樣的數據取出。打印在控制檯上。 5---關閉資源。
public class UDPReceiver { public static void main(String[] args) throws Exception { System.out.println("這是接收端"); //1:建立DatagramSocket對象,必須監聽一個端口。 DatagramSocket ds = new DatagramSocket(8080); while (true) { //2:建立一個DatagramPacket對象,存儲接收到的字節數據 DatagramPacket dp = new DatagramPacket(new byte[1024], 1024); //3:經過服務的receive方法將收到數據存入數據包中。 ds.receive(dp);//阻塞式方法。 //4:經過DatagramPacket對象獲取發送端傳來的數據 String data = new String(dp.getData(), 0, dp.getLength()); String ip = dp.getAddress().getHostAddress(); int port = dp.getPort(); System.out.println("來自" + ip + ":" + port + ":" + data); } //5:關閉DatagramSocket對象。 //ds.close(); } }
1.先打開客戶端,而後程序因爲ds.receive(dp);會進入等待
2.打開服務端後,客戶端會接收到服務端數據
3.若是客戶端在其餘的電腦上,對應好IP和端口,也能夠打印到其餘電腦上編程
控件是經過Idea拖拽的
主要邏輯是不變的,只是在按鈕點擊時進行數據的發送文本框中的字符串,關閉窗口時關閉服務數組
public class UDPSender { private JPanel mPanel1; private JButton mButton1; private JTextField mMsg; private DatagramSocket mDatagramSocket; public UDPSender() { mDatagramSocket = null; try { //1: 經過DatagramSocket建立對象:端口8081(此端口隨意) mDatagramSocket = new DatagramSocket(8081); } catch (Exception e) { e.printStackTrace(); } mButton1.addActionListener(e -> { //2: 使用DatagramPacket對象打包數據 byte[] buf = mMsg.getText().getBytes(); DatagramPacket dp = null; try { dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.56.1"), 8080); //3: 使用DatagramSocket對象發送數據包(字節數組,發送長度,ip,端口) mDatagramSocket.send(dp); } catch (Exception e1) { e1.printStackTrace(); } }); } public void close() { //4: 關閉DatagramSocket對象 mDatagramSocket.close(); } public static void main(String[] args) { JFrame frame = new JFrame("發送端"); frame.setSize(400, 400); frame.setLocation(300, 200); UDPSender UDPSender = new UDPSender(); frame.setContentPane(UDPSender.mPanel1); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { UDPSender.close(); } }); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }
public class UDPReceiver { private JPanel mPanel1; private JTextArea mTextArea1; public static void main(String[] args) { JFrame frame = new JFrame("接收端"); frame.setSize(400, 400); frame.setLocation(300, 200); UDPReceiver client = new UDPReceiver(); frame.setContentPane(client.mPanel1); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); StringBuilder sb = new StringBuilder(); //1:建立DatagramSocket對象,必須監聽一個端口。 DatagramSocket ds = null; try { ds = new DatagramSocket(8080); while (true) { //2:建立一個DatagramPacket對象,存儲接收到的字節數據 DatagramPacket dp = new DatagramPacket(new byte[1024], 1024); //3:經過服務的receive方法將收到數據存入數據包中。 ds.receive(dp);//阻塞式方法。 //4:經過DatagramPacket對象獲取發送端傳來的數據 String ip = dp.getAddress().getHostAddress(); String data = new String(dp.getData(), 0, dp.getLength()); int port = dp.getPort(); sb.append("來自" + ip + ":" + port + ":" + data+"\n"); client.mTextArea1.setText(sb.toString()); System.out.println(sb.toString()); } //5:關閉DatagramSocket對象。 //ds.close(); } catch (Exception e) { e.printStackTrace(); } } }
在設置-->關於手機-->狀態信息 中查看手機的ip(此處使用wifi測試)
在服務端要寫對應的ip。瀏覽器
鍵盤錄入做爲數據源,使用字符讀取流獲取數據,做爲發送數據服務器
public class UDPServerWithInput { //255,表明向該網段接收端發送 192.168.56.1~192.168.56.255 都能接收到 public static final String IP="192.168.56.1"; public static void main(String[] args) throws IOException { System.out.println("這是服務端"); //1: 經過DatagramSocket對象建立updSocket服務:端口8081(此端口隨意) DatagramSocket datagramSocket = new DatagramSocket(8081); //準備鍵盤錄入字符讀取流 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line = null; while ((line = br.readLine()) != null) { if ("886".equals(line)) { break; } byte[] buf = line.getBytes(); //2:使用DatagramPacket對象打包數據 DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName(IP), 8080); //3:使用DatagramSocket對象發送數據包(字節數組,發送長度,ip,端口) datagramSocket.send(dp); } //4:關閉DatagramSocket對象 datagramSocket.close(); } }
2.客戶端的Android代碼微信
本示例並不須要網絡權限!
必需要在子線程接收數據,否則報異常,因此使用handler進行控件刷新網絡
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @BindView(R.id.id_tv_ip) TextView mIdTvIp; Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { String obj = (String) msg.obj; mSb.append(obj + " "); mIdTvIp.setText(mSb.toString()); } }; private StringBuffer mSb; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); mSb = new StringBuffer(); new Thread() { @Override public void run() { try { //1:建立udp socket,創建端點。 DatagramSocket ds = new DatagramSocket(8080); while (true) { //2:定義數據包。用於存儲數據。 byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf, buf.length); //3:經過服務的receive方法將收到數據存入數據包中。 ds.receive(dp);//阻塞式方法。 //4:經過數據包的方法獲取其中的數據。 String data = new String(dp.getData(), 0, dp.getLength()); Message msg = Message.obtain(); msg.obj = data; mHandler.sendMessage(msg); } //5:關閉資源 //ds.close(); } catch (IOException e) { e.printStackTrace(); } } }.start(); } }
若是你想要對網絡傳輸有一點興趣,不妨親自試一下,用電腦控制手機或其餘電腦的感受還蠻不錯的。好了,就醬紫。
在騰訊雲上開啓服務,本地計算機去鏈接,以此測試TCP鏈接,這是java服務器端最底層的原理
實現場景1:客戶端(本機)輸入一個字符串,服務端返回相應的大寫字母
實現場景2:一個客戶端(本機)上傳文件到服務器,而後經過瀏覽器訪問
實現場景3:多個客戶端(本機)同時上傳文件到服務器(併發)
1.在服務器上有java環境 2.服務器上開放了測試使用的接口:本測試爲:8080端口 3.若是沒有服務器,開兩個cmd,本地也能夠測試,或者兩臺筆記本也能夠
獲取socket-->經過socket獲取讀流I--> 經過socket獲取寫流O-->I讀取後轉爲大寫,用寫流O輸出
public class TransServer { public static void main(String[] args) { try { //1.建立ServerSocket服務對象,並指定服務端口 ServerSocket serverSocket = new ServerSocket(8080); //2.經過accept方法獲取Socket對象 Socket socket = serverSocket.accept(); String ip = socket.getInetAddress().getHostAddress(); System.out.println(ip + "....connected");//日誌:打印鏈接的客戶端, //3.得到socket對象的字節輸入流,並轉化爲字符流,包裝成BufferedReader----用於讀取客戶端數據 BufferedReader brIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); //4.得到socket對象的字節輸出流,幷包裝成PrintWriter----用於發送給客戶端數據 PrintWriter pwOut = new PrintWriter(socket.getOutputStream(), true); String line = null; while ((line = brIn.readLine()) != null) { pwOut.println(line.toUpperCase());//將讀到的數據轉爲大寫,寫出到客戶端 System.out.println(ip + ":" + line.toUpperCase());//日誌:將讀到的數據轉爲大寫,打印出來 } //5.關閉資源 serverSocket.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
編譯 javac TransServer.java -encoding utf-8 運行:此時會進入等待 java TransServer
創建服務-->獲取鍵盤錄入--> 將數據發給服務端-->
獲取服務端返回的大寫數據--> 結束,關資源-->
public class TransClient { public static void main(String[] args) { String ip = "193.112.165.148"; int port = 8080; try { //1.建立Socket對象(ip,端口) Socket socket = new Socket(ip, port); //準備鍵盤錄入字符讀取流 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //3.得到socket對象的字節輸入流,並轉化爲字符流,包裝成BufferedReader----用於讀取服務端數據 BufferedReader brIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); //4.得到socket對象的字節輸出流,幷包裝成PrintWriter----用於發送給服務端數據 PrintWriter pwOut = new PrintWriter(socket.getOutputStream(), true); //注意這三個流的區別與做用:br--鍵盤錄入 brIn---讀取服務端數據 pwOut--發送給服務端數據 String line = null; while ((line = br.readLine()) != null) { if ("over".equals(line)) { break; } pwOut.println(line);//將鍵盤輸入內容發送給服務端 System.out.println("服務端:" + brIn.readLine());//讀取服務端的數據,並打印出來 } br.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * 做者:張風捷特烈 * 時間:2018/10/8 0008:11:50 * 郵箱:1981462002@qq.com * 說明:服務器端 */ public class UpLoadFileServer { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8080); while (true) { Socket socket = serverSocket.accept(); String ip = socket.getInetAddress().getHostAddress(); System.out.println(ip + "....connected"); InputStream is = socket.getInputStream(); String fileName = "F:\\ds.jpg"; FileOutputStream fos = new FileOutputStream(fileName); int len = 0; byte[] buf = new byte[1024]; while ((len = is.read(buf)) != -1) { fos.write(buf, 0, len); } OutputStream os = socket.getOutputStream(); os.write("OK".getBytes()); fos.close(); socket.close(); } } catch (IOException e) { e.printStackTrace(); } } /** * 獲取範圍隨機整數:如 rangeInt(1,9) * * @param s 前數(包括) * @param e 後數(包括) * @return 範圍隨機整數 */ public static int rangeInt(int s, int e) { int max = Math.max(s, e); int min = Math.min(s, e) - 1; return (int) (min + Math.ceil(Math.random() * (max - min))); } }
編譯 javac UpLoadFileServer.java -encoding utf-8 運行:此時會進入等待 java UpLoadFileServer
public class UpLoadFileClient { public static void main(String[] args) { String ip = "193.112.165.148"; int port = 8080; try { Socket socket = new Socket(ip, port); String path = "C:\\Users\\Administrator\\Desktop\\數據結構.jpg"; FileInputStream fis = new FileInputStream(path); OutputStream os = socket.getOutputStream(); byte[] buf = new byte[1024]; int len = 0; while ((len = fis.read(buf)) != -1) { os.write(buf, 0, len); } //告訴服務端數據已寫完 socket.shutdownOutput(); InputStream is = socket.getInputStream(); byte[] bufIn = new byte[1024]; int num = is.read(bufIn); System.out.println(new String(bufIn, 0, num)); fis.close(); is.close(); } catch (Exception e) { e.printStackTrace(); } } }
按照上面的代碼,每次只能有一我的上傳,後者等待,顯然是不合理的,應該多我的能夠併發執行。
這裏使用多線程,每次用戶鏈接都開啓一個線程來執行帶代碼。
/** * 做者:張風捷特烈 * 時間:2018/10/8 0008:11:50 * 郵箱:1981462002@qq.com * 說明:併發上傳 */ public class UpLoadFileServerCur { public static void main(String[] args) { try { ServerSocket serverSocket = new ServerSocket(8080); while (true) { new Thread(new FileThread(serverSocket.accept())).start(); } } catch (IOException e) { e.printStackTrace(); } } } class FileThread implements Runnable { private Socket mSocket; public FileThread(Socket socket) { mSocket = socket; } @Override public void run() { String ip = mSocket.getInetAddress().getHostAddress(); System.out.println(ip + "....connected"); try { InputStream is = mSocket.getInputStream(); String fileName = "F:\\ip" + ip + "-" + rangeInt(3000, 10000) + ".jpg"; FileOutputStream fos = new FileOutputStream(fileName); int len = 0; byte[] buf = new byte[1024]; while ((len = is.read(buf)) != -1) { fos.write(buf, 0, len); } OutputStream os = mSocket.getOutputStream(); os.write("上傳成功".getBytes()); fos.close(); mSocket.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 獲取範圍隨機整數:如 rangeInt(1,9) * * @param s 前數(包括) * @param e 後數(包括) * @return 範圍隨機整數 */ public static int rangeInt(int s, int e) { int max = Math.max(s, e); int min = Math.min(s, e) - 1; return (int) (min + Math.ceil(Math.random() * (max - min))); } }
項目源碼:Github:https://github.com/toly1994328/SocketDemo
項目源碼 | 日期 | 備註 |
---|---|---|
V0.1--無 | 2018-10-5 | 基於UDP的網絡數據傳輸測試(Java+Android) |
V0.2--無 | 2018-10-10 | 將UDP和TCP合爲一篇,並優化一些表述 |
筆名 | 微信 | 愛好 | |
---|---|---|---|
張風捷特烈 | 1981462002 | zdl1994328 | 語言 |
個人github | 個人簡書 | 個人CSDN | 我的網站 |
1----本文由張風捷特烈原創,轉載請註明 2----歡迎廣大編程愛好者共同交流 3----我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正 4----看到這裏,我在此感謝你的喜歡與支持