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