网络编程
网络编程指得是多台主机之间的数据通讯操作。
网络编程简介
网络的核心定义:两台以上的电脑称为网络,多台电脑之间的通讯。
网络连接的目的不仅仅是为了进行电脑的串联,更多的情况下是彼此之间的数据通讯,而在通讯的实现上,产生了一系列处理协议:IP、TCP、UDP,所谓的网络编程实现的就是数据通讯操作,只不过这个通讯操作分为客户端与服务器端。
于是针对于网络程序的开发就有两个模型:
- C/S(Client/Server、客户端与服务器端):要开发出两套程序,假设服务器端发生改变客户端也应该进行更新处理,这种开发可以由开发者自定义传输协议,并且使用一些私密的端口,安全性较高,但开发维护成本较高。
- B/S(Browse/Server、浏览器与服务器端):只开发一套服务器程序,利用浏览器作为客户端进行访问,开发维护成本较低(只有一套程序),但是由于其使用公共HTTP协议以及80端口,所以安全性难以保障,现在的开发基本上以“B/S”为主。
本次学习的网络编程主要为C/S程序模型,分为两种开发:TCP(可靠数据连接)、UDP(不可靠数据连接)。
Echo程序模型
TCP程序开发时网络程序的最基本的开发模型,其核心特点是使用两个类实现数据交互处理:ServerSocket(服务器端)、Socket(客户端)。
ServerSocket的主要目的是设置服务器的监听端口,而Socket需要指明要连接的服务器地址与端口。
Echo模型
简单Echo程序实现
- 实现服务器端
package per.lyz.demo.server;
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); //服务器端监听端口
System.out.println("等待客户端连接。。。。。。");
Socket client = server.accept(); //有客户端连接
// 1. 首先需要接受客户端发送的信息,处理之后发送回客户端
Scanner scan = new Scanner(client.getInputStream()); //客户端输入流
scan.useDelimiter("\n"); //设置分割符
PrintStream out = new PrintStream(client.getOutputStream()); //客户端输出流
boolean flag = true; //循环标记
while(flag) {
if(scan.hasNext()) { //有数据发送
String val = scan.next().trim(); //接收发送数据
if("byebye".equalsIgnoreCase(val)) {
out.println("See you next time!");
flag = false; //结束循环
} else{
out.println("[Echo]" + val);
}
}
}
client.close();
server.close();
}
}
- 实现客户端程序
package per.lyz.demo.Client;
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_INPUT = new BufferedReader(new InputStreamReader(System.in));
public static void main(String[] args) throws Exception{
Socket client = new Socket("localhost", 9999); //定义服务端的连接信息
// 现在客户端需要有输入输出操作支持,所以依然需要准备出Scanner与PrintWriter
Scanner scan = new Scanner(client.getInputStream()); //接收服务器端输入内容
scan.useDelimiter("\n");
PrintStream out = new PrintStream(client.getOutputStream()); //向服务端发送内容
boolean flag = true;
while(flag) {
String input = getString("请输入要发送数据内容:").trim();
out.println(input); //加换行
if(scan.hasNext()) { //服务器端有回应
System.out.println(scan.next());
}
if("byebye".equalsIgnoreCase(input)) {
flag = false;
}
}
scan.close();
out.close();
client.close();
}
public static String getString(String prompt) throws Exception {
System.out.print(prompt);
String str = KEYBOARD_INPUT.readLine();
return str;
}
}
此时就实现了最基础的C/S数据通讯操作。
BIO处理模型(多线程与网络编程)
上述程序存在严重性能缺陷,即服务器只能够为一个线程提供Echo服务,如果说现在服务器需要多人访问时,其他人都在等待连接。
所以现在可以发现单线程服务器开发本身就是一种不合理的做法,此时最好的解决办法就是将每一个连接到服务器上的客户端都通过一个线程对象来进行处理,即服务器上启动多个线程,每个线程单独为每一个客户端实现Echo支持。
修改服务器端程序(不必修改客户端)
package per.lyz.demo.server;
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 scan = null;
private PrintStream out = null;
private boolean flag = true; //循环标记
public ClientThread(Socket client) throws Exception{
this.client = client;
this.scan = new Scanner(client.getInputStream()); //客户端输入流
this.scan.useDelimiter("\n"); //设置分割符
this.out = new PrintStream(client.getOutputStream()); //客户端输出流
}
@Override
public void run() {
while(this.flag) {
if(scan.hasNext()) { //有数据发送
String val = scan.next().trim(); //接收发送数据
if("byebye".equalsIgnoreCase(val)) {
out.println("See you next time!");
this.flag = false; //结束循环
} else{
out.println("[Echo]" + val);
}
}
}
try {
client.close();
scan.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception{
@SuppressWarnings("resource")
ServerSocket server = new ServerSocket(9999); //服务器端监听端口
System.out.println("等待客户端连接。。。。。。");
boolean flag = true;
while(flag) {
Socket client = server.accept();
new Thread(new ClientThread(client)).start();;
}
}
}
如果在此类代码中追加一些集合的数据控制,就可以实现一个80年代的聊天室。
UDP程序
上述程序均属于TCP程序,TCP程序最大的特点是可靠的数据连接,但是网络程序开发中还存在UDP程序(基于数据报的网络编程实现),要想实现UDP程序需要用到两个类:DatagramPacket(数据内容)、DatagramSocket(网络发送接收)。
数据报就好比发送的短消息,客户端是否接收到与发送者无关。
UDP客户端
package per.lyz.demo.Client;
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("客户端等待接收发送的消息。。。。。。。。");
client.receive(packet); //接收消息,所有消息都在data字节数组中
System.out.println("接收消息内容为:"+ new String(data,0,packet.getLength()));
client.close();
}
}
UDP服务端
package per.lyz.demo.server;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPServer {
public static void main(String[] args) throws Exception {
DatagramSocket server = new DatagramSocket(9000); //监听9000端口
String str = "www.baidu.com!";
DatagramPacket packet = new DatagramPacket(str.getBytes(),0,str.length(),InetAddress.getByName("localhost"),9999); //数据包
server.send(packet);
System.out.println("发送完成!");
server.close();
}
}
UDP发送的数据是不可靠的,但是由于TCP需要保证可靠的连接所以需要的服务器资源更多。