引言:網(wǎng)絡(luò)編程作為開發(fā)服務(wù)端和客戶端必不可少的技術(shù)之一陆馁,在Java中也實(shí)現(xiàn)的十分完備,今天對(duì)Java的網(wǎng)絡(luò)編程方面的只是做一個(gè)簡(jiǎn)單的總結(jié)合愈,方便以后學(xué)習(xí):
一:什么是網(wǎng)絡(luò)編程叮贩?
1:概念:通過(guò)使用套接字來(lái)達(dá)到進(jìn)程間通信目的的編程就是網(wǎng)絡(luò)編程。windows提供的基于網(wǎng)絡(luò)編程的就是套接字也就是winsock佛析,同時(shí)Winpcap也是一個(gè)比較方便的工具妇汗。
2:幾個(gè)常用概念:
網(wǎng)絡(luò)模型:描述網(wǎng)絡(luò)的結(jié)構(gòu)原理和工作原
OSI參考模型:七層
Internet網(wǎng)絡(luò)模型:四層
網(wǎng)絡(luò)協(xié)議:指定層上進(jìn)行數(shù)據(jù)交換的規(guī)則。
Internet的網(wǎng)絡(luò)層協(xié)議:IP協(xié)議说莫;DNS協(xié)議(輔助協(xié)議)
Internet的傳輸層協(xié)議:TCP協(xié)議,UDP協(xié)議
3:Java的Socket通信模型:
相關(guān)類介紹:
- A:ServerSocket:用java.net.ServerSocket實(shí)現(xiàn)java服務(wù)通過(guò)TCP/IP監(jiān)聽客戶端連接‘寞焙;
創(chuàng)建監(jiān)聽指定端口號(hào):
ServerSocket serverSocket = new ServerSocket(9000);
要獲取請(qǐng)求的連接需要用ServerSocket.accept()方法储狭。該方法返回一個(gè)Socket類,該類具有普通java Socket類的所有特性捣郊。每個(gè)調(diào)用了accept()方法的ServerSocket都只獲得一個(gè)連接辽狈;
Socket socket = ServerSocket.accept();
- B:Socket:當(dāng)我們想要在Java中使用TCP/IP通過(guò)網(wǎng)絡(luò)連接到服務(wù)器時(shí),就需要?jiǎng)?chuàng)建java.net.Socket對(duì)象并連接到服務(wù)器
創(chuàng)建連接到指定地址和端口的Socket;
Socket socket = new Socket("78.46.84.171", 80);
通過(guò)Socket發(fā)送數(shù)據(jù):
OutputStream out = socket.getOutputStream();
out.write("some data".getBytes());
out.flush();
out.close();
不要忘記調(diào)用輸出流的flush()方法呛牲;操作系統(tǒng)底層的TCP/IP實(shí)現(xiàn)會(huì)先將數(shù)據(jù)放入一個(gè)更大的數(shù)據(jù)緩存塊中刮萌,而緩存塊的大小是與TCP/IP的數(shù)據(jù)包大小相適應(yīng)的。(譯者注:調(diào)用flush()方法只是將數(shù)據(jù)寫入操作系統(tǒng)緩存中娘扩,并不保證數(shù)據(jù)會(huì)立即發(fā)送)
通過(guò)Socket讀取數(shù)據(jù)
InputStream in = socket.getInputStream();
int data = in.read();
需要注意的是着茸,從Socket的輸入流中讀取數(shù)據(jù)并不能讀取文件那樣,一直調(diào)用read()方法直到返回-1為止琐旁,因?yàn)閷?duì)Socket而言涮阔,只有當(dāng)服務(wù)端關(guān)閉連接時(shí),Socket的輸入流才會(huì)返回-1灰殴,而是事實(shí)上服務(wù)器并不會(huì)不停地關(guān)閉連接敬特。假設(shè)我們想要通過(guò)一個(gè)連接發(fā)送多個(gè)請(qǐng)求,那么在這種情況下關(guān)閉連接就顯得非常愚蠢牺陶。
因此伟阔,從Socket的輸入流中讀取數(shù)據(jù)時(shí)我們必須要知道需要讀取的字節(jié)數(shù),這可以通過(guò)讓服務(wù)器在數(shù)據(jù)中告知發(fā)送了多少字節(jié)來(lái)實(shí)現(xiàn)掰伸,也可以采用在數(shù)據(jù)末尾設(shè)置特殊字符標(biāo)記的方式連實(shí)現(xiàn)皱炉。
- C:URL和URLConnection類
在java.net包中包含兩個(gè)有趣的類:URL類和URLConnection類。這兩個(gè)類可以用來(lái)創(chuàng)建客戶端到web服務(wù)器(HTTP服務(wù)器)的連接碱工,當(dāng)然也可建立客戶端可本地文件之間的連接娃承,
URL url = new URL("http://jenkov.com");創(chuàng)建一個(gè)到到改地址的URL
URLConnection urlConnection = url.openConnection()
InputStream input = urlConnection.getInputStream();
int data = input.read();
while(data != -1){
System.out.print((char) data);
data = input.read();
}
input.close();
- D:InetAddress類:是 Java 對(duì) IP 地址的封裝奏夫。里面包含了很多關(guān)于ID地址的信息;這個(gè)類的實(shí)例經(jīng)常和 UDP DatagramSockets 和 Socket历筝,ServerSocket 類一起使用酗昼。
- E:DatagramSocket代表UDP協(xié)議的Socket,DatagramSocket本身只是碼頭梳猪,不維護(hù)狀態(tài)麻削,不能產(chǎn)生IO流,它的唯一作用就是接收和發(fā)送數(shù)據(jù)報(bào)春弥,Java使用DatagramPacket來(lái)代表數(shù)據(jù)報(bào)呛哟,DatagramSocket接收和發(fā)送的數(shù)據(jù)都是通過(guò)DatagramPacket對(duì)象完成的。
- DatagramPacket:基于UDP的數(shù)據(jù)包類匿沛,里面可以填寫很多信息扫责,地址,端口逃呼,數(shù)據(jù)等鳖孤;
.由這兩個(gè)類所有構(gòu)成的網(wǎng)絡(luò)鏈接是基于UDP協(xié)議,是一種不可靠的協(xié)議抡笼。
二:具體代碼實(shí)現(xiàn):
1:基于TCP的客服端和服務(wù)端通信:
服務(wù)端代碼:
public class Server {
public static void main(String[] args) {
try {
//創(chuàng)建一個(gè)服務(wù)端監(jiān)聽8888端口
ServerSocket server = new ServerSocket(8888);
//調(diào)用accept方法開始監(jiān)聽苏揣,等待客戶端建立連接
System.out.println("服務(wù)段開始監(jiān)聽...............");
Socket socket = server.accept();
InputStream in = socket.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
BufferedReader buff = new BufferedReader(reader);
String line=null;
while((line=buff.readLine())!=null){
System.out.println("我是服務(wù)端:客戶端說(shuō):"+line);
}
socket.shutdownInput();
OutputStream out = socket.getOutputStream();
PrintWriter rw = new PrintWriter(out);
rw.write("你好客戶端呀!M埔觥F叫佟!");
rw.close();
buff.close();
reader.close();
in.close();
server.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客戶端代碼:
public class Client {
public static void main(String[] args) {
try {
Socket client = new Socket("127.0.0.1",8888);
OutputStream out = client.getOutputStream();
PrintWriter pw = new PrintWriter(out);
pw.write("用戶名:admin;密碼:123");
pw.flush();
//記住關(guān)閉
client.shutdownOutput();
/**
* 讀取服務(wù)端發(fā)送來(lái)的信息
*/
InputStream in = client.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
BufferedReader buf = new BufferedReader(reader);
int data = buf.read();
while(data!=-1){
System.out.print((char)data);
data=buf.read();
}
buf.close();
reader.close();
in.close();
pw.close();
out.close();
client.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2:基于UDP的客服端和服務(wù)端連接:
服務(wù)端:
public class Server {
public static void main(String[] args) throws IOException {
try {
//1:創(chuàng)建服務(wù)的端的Socket并指定端口
DatagramSocket socket = new DatagramSocket(8800);
byte[] arr = new byte[1024];
DatagramPacket packet = new DatagramPacket(arr, arr.length);
System.out.println("服務(wù)端已經(jīng)打開了:");
socket.receive(packet);
String str = new String(arr);
System.out.println(str);
//1:向客戶端發(fā)送信息時(shí)藏古,也需要知道客戶端的地址端口號(hào)等信息
InetAddress address = packet.getAddress();
int port1 = packet.getPort();
System.out.println(port1+"==============");
byte[] arr1 = "歡迎您".getBytes();
DatagramPacket datapacketR = new DatagramPacket(arr1,arr1.length,address,port1);
socket.send(datapacketR);
socket.shutdown();
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客戶端:
public class Client {
public static void main(String[] args) throws IOException {
//1:創(chuàng)建服務(wù)的端的地址增炭,并定義端口號(hào),并定義數(shù)據(jù)
InetAddress inetAddress = InetAddress.getByName("localhost");
int port = 8800;
byte[] bytes = "用戶名:陳鵬拧晕,密碼:1234".getBytes();
//2:創(chuàng)建數(shù)據(jù)報(bào)弟跑,包含發(fā)送的地址信息
DatagramPacket datapacket = new DatagramPacket(bytes, bytes.length,inetAddress,port);
//3:創(chuàng)建Socket
DatagramSocket socket = new DatagramSocket();
socket.send(datapacket);
//接受服務(wù)端發(fā)送來(lái)的信息
byte[] arr = new byte[1024];
DatagramPacket datapacketR = new DatagramPacket(arr,arr.length);
socket.receive(datapacketR);
String data = new String(arr);
System.out.println("我是客戶端,服務(wù)器端響應(yīng)的數(shù)據(jù)是:"+data);
}
}
三:自定協(xié)議:
需要考慮三點(diǎn):
- 1:客戶端到服務(wù)端的往返通訊
- 1.在分開往返中發(fā)送頭信息防症;
- 2.將消息分成更小的數(shù)據(jù)塊孟辑。
- 2:區(qū)分請(qǐng)求結(jié)束和響應(yīng)結(jié)束。
- 1:在請(qǐng)求頭中包含請(qǐng)求信息長(zhǎng)度
- 2:在請(qǐng)求體中增加標(biāo)志位
- 3:防火墻穿透
- 1:基于HTTP協(xié)議上建立協(xié)議蔫敲;