java网络编程之Socket编程
概念
網絡編程分為BIO(傳統IO)、NIO、AIO。Socket編程屬于BIO這種傳統IO。
InetAddress
java.net.InetAddress是JAVA中管理IP地址的類,常用
public static void main(String[] args) throws UnknownHostException {InetAdressDemo.getLocalHost();System.out.println("---------------------------");getHostByName("Lenovo-Autumn");}/*** 獲取主機ip和主機名* @throws UnknownHostException*/public static void getLocalHost() throws UnknownHostException {//根據InetAddress獲取主機名和主機ipInetAddress localHost = InetAddress.getLocalHost();System.out.println(localHost); //打印:Lenovo-Autumn/192.168.56.1//根據getLocalHost()返回的值獲取ip和主機名String hostName = localHost.getHostName();String hostAddress = localHost.getHostAddress();System.out.println(hostName); //打印 Lenovo-AutumnSystem.out.println(hostAddress); //打印 192.168.56.1//根據切割獲取主機名和ipString[] str = localHost.toString().split("/");System.out.println(str[0]); //打印 Lenovo-AutumnSystem.out.println(str[1]); //打印 192.168.56.1 }/*** 根據主機名稱獲取ip地址* @param otherName 主機名(可以是局域網中的機器名或者是域名或者是ip)* @throws UnknownHostException*/public static void getHostByName(String otherName) throws UnknownHostException {InetAddress otherHost = InetAddress.getByName(otherName);String hostName = otherHost.getHostName();String hostAddress = otherHost.getHostAddress();System.out.println(hostName); //打印 Lenovo-AutumnSystem.out.println(hostAddress); //打印 192.168.56.1System.out.println(otherHost); //打印:Lenovo-Autumn/192.168.56.1}
code
UDP
發送數據時必須指定接收端的IP地址和端口號,就好像發送貨物的集裝箱上面必須標明接收人的地址一樣。
接收端不需要明確知道數據的來源,只需要接收到數據即可。
java.net.DatagramPackage
構造函數:
第一種是用來接受的數據包,不需要指定IP和端口,第二種是用來發送的數據包需要指定ip和端口
方法:
獲取ip地址,獲取服務器ip或者客戶端ip
返回端口號,獲取發送方或者
返回數據緩沖區
返回要發送或接受的數據包大小
java.net.DatagramSocket
DatagramPacket數據包的作用就如同是“集裝箱”,可以將發送端或者接收端的數據封裝起來。然而運輸貨物只有“集裝箱”是不夠的,還需要有碼頭。在程序中需要實現通信只有DatagramPacket數據包也同樣不行,為此JDK中提供的一個DatagramSocket類。DatagramSocket類的作用就類似于碼頭,使用這個類的實例對象就可以發送和接收DatagramPacket數據包,發送數據的過程如下圖所示。
構造函數:
用來創建發送端的DatagramSocket對象
用來創建接收端的DatagramSocket對象
方法:
發送數據報包
接收數據報包
發送端
1, ?創建DatagramSocket對象
DatagramSocket()
2,創建DatagramPacket對象,封裝數據,并指定ip和端口。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
3,發送數據
socket.send(DatagramPacket dp)
4,釋放流資源
ds.close();
接收端
1,創建DatagramSocket對象,只需要指定端口
DatagramSocket(port)
2,創建DatagramPacket對象
DatagramPacket(byte[] data, int?length)
3,接收數據存儲到DatagramPacket對象中
receive(DatagramPackage dp)
4,獲取DatagramPacket對象的內容
new String(data,0,dp.getLength());
5,釋放流資源
ds.close();
/*** 實現udp發送端* 用java.net.DatagramPackage封裝數據* 用java.net.DatagramSocket發送數據** 實現步驟* 1.用DatagramPackage對象,封裝數據,接受的地址和端口* 2.創建DatagramSocket* 3.調用DatagramSocket對象send方法,發送數據* 4.關閉資源** DatagramPackage構造函數* DatagramPacket(byte[] buf, int length, InetAddress address, int port)* DatagramSocket構造函數* DatagramSocket()* 方法:send(DatagramPacket d)* Created by Autumn on 2018/2/5.*/ public class UdpSend {public static void main(String[] args) throws Exception {Scanner scanner = new Scanner(System.in);//獲取地址InetAddress inet = InetAddress.getByName("127.0.0.1");//創建DatagramSocket,負責接受和發送數據DatagramSocket ds = new DatagramSocket();while(true){String msg = scanner.nextLine();//創建數據包對象對象byte[] data = msg.getBytes();//封裝數據,接受的地址和端口DatagramPacket dp = new DatagramPacket(data,data.length,inet,6000);//發送數據包 ds.send(dp);if(msg.equals("exit")){break;}}//關閉 ds.close();} }/*** 實現udp接收端* 用java.net.DatagramPackage 接受數據* 用java.net.DatagramSocket 接受數據包** 步驟* 1.創建DatagramSocket對象,綁定端口號(要和發送端端口一致)* 2.創建字節數組用來接受數據* 3.創建數據對象包DatagramPackage* 4.創建DatagramSocket* receive(DatagramPackage dp)接受數據,將數據封裝如dp中* 5.拆包* 發送端的ip地址(DatagramPackage.get)* 接受到的字節數組* 發送的端口號* 6.關閉資源* Created by Autumn on 2018/2/5.*/ public class UdpReceive {public static void main(String[] args) throws IOException {//創建數據包傳輸的對象,并綁定端口號DatagramSocket ds = new DatagramSocket(6000);//創建字節數組byte[] data = new byte[1024];while(true){//創建數據包對象,傳遞字節數組DatagramPacket dp = new DatagramPacket(data,data.length);//調用ds對象的receive接受數據包,receive()有線程阻塞效果會一直等待接受數據 ds.receive(dp);//獲取數據包大小int len = dp.getLength();//獲取發送端的ip地址InetAddress sendAddress = dp.getAddress();String sendHostAddress = sendAddress.getHostAddress();//System.out.println(sendHostAddress);//獲取發送端端口號int port = dp.getPort();//System.out.println(port);//System.out.println(new String(data)); //直接打印1024個字節的字符串,有很多空格System.out.println(sendHostAddress+":"+port+" "+new String(data,0,len)); //這樣打印沒有多余的空格if(new String(data,0,len).equals("exit")){break;}}//關閉 ds.close();} }code
TCP
ServerSocket類,用于表示服務器端,Socket類,用于表示客戶端。建立連接后用流進行輸入和輸出
?
ServerSocket
實例化一個ServerSocket類,指定端口號
監聽并接受此套接字的連接
返回此服務器套接字的本地地址
Socket
實例化一個Socket并指定ip和端口
返回一個輸出流,用于客戶端發送數據
返回一個輸入流,用于服務器端接受數據
返回ip地址(服務器端的地址)
返回端口號
客戶端
1,創建客戶端的Socket對象,指定服務器IP和端口號
Socket(String host, int port) ? ? ?
2,獲取Socket的輸出流對象
getOutputStream();?
3,寫數據給服務器
out.write("服務器數據".getBytes());
4,關閉流資源
socket.close();
服務器端
1,創建服務器端ServerSocket對象,指定服務器端端口號
ServerSocket(int port)
2,開啟服務器,等待著客戶端Socket對象的連接,如有客戶端連接,返回客戶端的Socket對象
accept()
3,通過客戶端的Socket對象,獲取客戶端的輸入流,為了實現獲取客戶端發來的數據
socket.getInputStream();
4,通過客戶端的輸入流,獲取流中的數據
byte[] data = new byte[1024];
int len = inputStream.read(data);
System.out.println(new String(data,0,len));
5,通過客戶端的Socket對象,獲取客戶端的輸出流,為了實現給客戶端反饋信息
6,通過客戶端的輸出流,寫數據到流中
7,關閉流資源
socket.close();
serverSocket.close();
用cmd命令telnet實現和SocketServer互動
import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket;public class TCPServer {/*同步阻塞*/public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(8888);System.out.println("服務端啟動成功...");while(true){/*一次只能處理一個連接,在一個連接沒關閉前無法接收第二個連接,在這里開一個框發送數無問題,開放兩個框時會出現第二個無反應*/Socket socket = serverSocket.accept(); //這里會阻塞等待連接接入 cmd中telnet 127.0.0.1 8888即代表連接System.out.println("新客戶端連接成功....");InputStream inputStream = socket.getInputStream();while(true) {byte[] data = new byte[1024];System.out.println("正在等待數據...");int len = inputStream.read(data); //這里會阻塞,等待數據,如果直接關閉cmd窗口會因為關閉socket通道導致len返回-1 cmd中ctrl+]進入到telnet操作模式send value發送數據if (len != -1){System.out.println(new String(data, 0,len, "GBK")); //用GBK是因為CMD窗口命令發送的數據是GBK編碼}else{break;}}}//socket.close();//serverSocket.close();//System.out.println("服務器端關閉...."); } }連接服務端?
向服務端發送數據
再開一個cmd然后telnet發送數據,發現無反應。必須關閉第一個cmd才能連接成功。
線程池改進,能并發訪問
用ExecutorService線程池實現每一個連接創建一個新的線程
import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class TCPServer {/*異步阻塞*/public static void main(String[] args) throws IOException {ExecutorService threadPool = Executors.newCachedThreadPool(); //線程池ServerSocket serverSocket = new ServerSocket(8888);System.out.println("服務端啟動成功...");while(true){/*一次只能處理一個連接,在一個連接沒關閉前無法接收第二個連接,在這里開一個框發送數無問題,開放兩個框時會出現第二個無反應*/System.out.println("等待客戶端連接...");final Socket socket = serverSocket.accept(); //這里會阻塞等待連接接入 cmd中telnet 127.0.0.1 8888即代表連接threadPool.execute(new Runnable() { //啟動一個線程public void run() {try {System.out.println("新客戶端連接成功....");InputStream inputStream = socket.getInputStream();while(true) {byte[] data = new byte[1024];System.out.println("正在等待數據...");int len = inputStream.read(data); //這里會阻塞,等待數據,如果直接關閉cmd窗口會因為關閉socket通道導致len返回-1 cmd中ctrl+]進入到telnet操作模式send value發送數據if (len != -1){System.out.println(Thread.currentThread()+new String(data, 0,len, "GBK")); //用GBK是因為CMD窗口命令發送的數據是GBK編碼}else{System.out.println("break循環");break;}}System.out.println("一次Socket連接關閉");} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}});}//socket.close();//serverSocket.close();//System.out.println("服務器端關閉...."); } }優點:傳輸質量好(所以BIO適合傳輸連接少但是數據量大的請求)
缺點:每一個連接都占用一個線程,很占用系統資源。
tip:所有的調優都關聯到系統資源(IO、存儲、內存、CPU)
code
轉載于:https://www.cnblogs.com/aeolian/p/8417916.html
總結
以上是生活随笔為你收集整理的java网络编程之Socket编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入iOS系统底层之CPU寄存器介绍
- 下一篇: FIR调用DSP48E_05