??所謂的網(wǎng)絡編程指的是多臺主機之間的數(shù)據(jù)通訊操作。
網(wǎng)絡編程簡介
??網(wǎng)絡的核心定義在于:有兩臺以上的電腦就稱為網(wǎng)絡。實際上在世界上產(chǎn)生的第一臺電腦之后就有人開始去思考如何生產(chǎn)更多的電腦并且進行有效連接。
??網(wǎng)絡連接的目的不僅僅是為了進行電腦的串聯(lián),更多的情況下是為了進行彼此之間的數(shù)據(jù)通訊,包括現(xiàn)在所謂的網(wǎng)絡游戲本質上還是網(wǎng)絡通訊的問題,而在通訊的實現(xiàn)上就產(chǎn)生了一系列的處理協(xié)議:IP、TCP、UDP等等,也就是說所謂的網(wǎng)絡編程,實際上實現(xiàn)的就是一個數(shù)據(jù)的通訊操作而已,只不過這個通訊操作需要分為客戶端和服務端。
??于是針對網(wǎng)絡程序的開發(fā)就有了兩種模型:
- C/S(Client/Server、客戶端與服務端):要開發(fā)出兩套程序,一套程序為客戶端,另外一套為服務端,如果現(xiàn)在服務端發(fā)生了改變之后客戶端也應該進行更新處理,這種開發(fā)可以由開發(fā)者自定義傳輸協(xié)議,并且使用一些比較私密的端口,所以安全性是比較高的,但是開發(fā)與維護成本比較高;
-
B/S(Browser/Server、瀏覽器與服務端):只開發(fā)一套服務端的程序,而后利用瀏覽器作為客戶端進行訪問,這種開發(fā)與維護的成本較低(只有一套程序),但是由于其使用的是公共的HTTP協(xié)議并且使用的公共的80端口,所以其安全性相對較差,現(xiàn)在的開發(fā)基本上以“B/S”結構為主。
??本次所要的網(wǎng)絡編程主要就是C/S程序模型,其分為兩種開發(fā):TCP(可靠的數(shù)據(jù)連接)、UDP(不可靠的數(shù)據(jù)連接);
TCP程序的基本實現(xiàn)
??TCP的程序開發(fā)是網(wǎng)絡程序的最基本的開發(fā)模型,其核心的特點是使用兩個類實現(xiàn)數(shù)據(jù)的交互處理:ServerSocket(服務端)、Socket(客戶端)。

??ServerSocket的主要目的是設置服務器的監(jiān)聽端口,而Socket需要指明要連接的服務器地址和端口。下面實現(xiàn)一個最簡單的數(shù)據(jù)處理操作,即Echo程序實現(xiàn)。

范例:實現(xiàn)服務端的定義
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class EchoServer {
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(9999);//設置服務端的監(jiān)聽端口
System.out.println("等待客戶端連接................");
Socket client = server.accept();//有客戶端連接
//1、首先需要先接收客戶端發(fā)來的信息,而后才可以將信息處理后發(fā)送回客戶端
Scanner scanner = new Scanner(client.getInputStream());//客戶端輸入流
scanner.useDelimiter("\n");//設置分隔符
PrintStream out = new PrintStream(client.getOutputStream());//客戶端輸出流
boolean flag = true;//循環(huán)標記
while (flag) {
if (scanner.hasNext()) {//現(xiàn)在有數(shù)據(jù)發(fā)送
String val = scanner.next().trim();//接收發(fā)送的數(shù)據(jù)
if ("exit".equalsIgnoreCase(val)) {
out.println("bye");
flag = false;
} else {
out.println("【ECHO】" + val);
out.flush();
}
}
}
scanner.close();
out.close();
client.close();
server.close();
}
}
范例:實現(xiàn)客戶端的定義
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class EchoClient {
private static final BufferedReader KEYBOARD_BUF = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws Exception{
Socket client = new Socket("localhost",9999);//定義服務端連接信息
//現(xiàn)在的客戶端需要有輸入與輸出的操作支持,所以依然要準備出Scanner與PrintWriter
Scanner scanner=new Scanner(client.getInputStream());//接收服務端的輸入內(nèi)容
scanner.useDelimiter("\n");
PrintStream out = new PrintStream(client.getOutputStream());//向服務器發(fā)送內(nèi)容
boolean flag=true;//循環(huán)標記
while (flag){
String input = getString("請輸入要發(fā)送的內(nèi)容:");
out.println(input);//加換行
if(scanner.hasNext()){//服務端有回應了
System.out.println(scanner.next());
}
if("exit".equalsIgnoreCase(input)){
flag =false;
}
}
scanner.close();
out.close();
client.close();
}
public static String getString(String prompt)throws Exception{
System.out.print(prompt);
String str=KEYBOARD_BUF.readLine();
return str;
}
}
??此時就實現(xiàn)了一個最基礎的客戶端與服務端之間的數(shù)據(jù)通訊操作。
多線程與網(wǎng)絡開發(fā)
??現(xiàn)在盡管已經(jīng)實現(xiàn)了一個標準的網(wǎng)絡程序開發(fā),但是在整個的開發(fā)過程之中存在嚴重的性能缺陷,因為該服務器只能夠為一個線程提供Echo服務,如果說現(xiàn)在的服務器需要有多人進行連接訪問的時候,那么其他的使用者將無法連接(等待連接)。
??所以現(xiàn)在就可以發(fā)現(xiàn)單線程的服務器開發(fā)本身就是一種不合理的做法,那么此時最好的解決方案將每一個連接到服務器上的客戶端都通過一個線程對象來進行處理,即:服務器上啟動多個線程,每一個線程單獨為每一個客戶端實現(xiàn)Echo服務支持。

范例:修改服務器端程序
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class EchoServer {
private static class ClientThread implements Runnable {
private Socket client = null;
private Scanner scanner = null;
private PrintStream out = null;
private boolean flag = true;//循環(huán)標記
public ClientThread(Socket client) throws Exception {
this.client = client;
scanner = new Scanner(client.getInputStream());//客戶端輸入流
scanner.useDelimiter("\n");//設置分隔符
out = new PrintStream(client.getOutputStream());//客戶端輸出流
}
@Override
public void run() {
while (flag) {
if (scanner.hasNext()) {//現(xiàn)在有數(shù)據(jù)發(fā)送
String val = scanner.next().trim();//接收發(fā)送的數(shù)據(jù)
if ("exit".equalsIgnoreCase(val)) {
out.println("bye");
flag = false;
} else {
out.println("【ECHO】" + val);
out.flush();
}
}
}
scanner.close();
out.close();
try {
if (client != null)
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(9999);//設置服務端的監(jiān)聽端口
System.out.println("等待客戶端連接................");
//1、首先需要先接收客戶端發(fā)來的信息,而后才可以將信息處理后發(fā)送回客戶端
boolean flag = true;//循環(huán)標記
while (flag) {
new Thread(new ClientThread(server.accept())).start();
}
server.close();
}
}
??如果你在這類的代碼中再追加一些集合的數(shù)據(jù)控制,實際上就可以實現(xiàn)一個80年代的聊天室了。
數(shù)據(jù)報發(fā)送與接收(UDP)
??之前所見到的都屬于TCP程序開發(fā)范疇,TCP程序最大的特點是可靠的網(wǎng)絡連接,但是在網(wǎng)絡程序開發(fā)之中還存在一種UDP程序,基于數(shù)據(jù)報的網(wǎng)絡編程實現(xiàn),如果想要實現(xiàn)UDP程序需要兩個類:DatagramPacket(數(shù)據(jù)內(nèi)容)、DatagramSocket(網(wǎng)絡的發(fā)送與接收)。數(shù)據(jù)報就好比發(fā)送的短消息一樣,客戶端是否收到與發(fā)送者無關。
范例:實現(xiàn)一個UDP客戶端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPClient {
public static void main(String[] args) throws Exception{
DatagramSocket client = new DatagramSocket(9999);//連接到9999端口
byte [] data = new byte [1024];//接收信息
DatagramPacket packet = new DatagramPacket(data,data.length);
System.out.println("客戶端等待接收發(fā)送的消息............");
client.receive(packet);
System.out.println("接收的消息內(nèi)容為:"+new String(data,0,packet.getLength()));
}
}
范例:實現(xiàn)一個UDP服務端
import java.net.InetAddress;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer {
public static void main(String[] args)throws Exception {
DatagramSocket server = new DatagramSocket(9000);//連接到9999端口
String str="www.baidu.com";
DatagramPacket packet = new DatagramPacket(str.getBytes(),0, str.length(),InetAddress.getByName("localhost"),9999);
server.send(packet);
System.out.println("消息發(fā)送完畢.....");
server.close();
}
}
??UDP發(fā)送的數(shù)據(jù)一定是不可靠的,但是TCP由于需要保證可靠的連接,所以所需要的服務器資源就越多。