所谓的网络编程指的就说多台主机之间的数据通讯操作。
网络编程简介
网络的核心定义在于:有两台以上的电脑就成为网络。
网络连接的目的不仅仅是为了进行电脑的串联,更多的情况下是为了彼此间的数据通讯,包括现在所谓的网络游戏本质上还是网络通讯的问题,而在通讯的实现上就产生了一系列的处理协议:IP、TCP、UDP等等。网络编程实现的就说一个数据的通讯操作,只不过这个通讯操作需要分为客户端与服务器端。
于是针对网络程序的开发就有了两种模型:
- C/S(Client/Server、客户端与服务器端):要开发出两套程序。一套为客户端,一套为服务器端。
现在服务器端发生了改变之后客户端也应该进行更新处理,这种开发可以由开发者自定义传输协议,并且使用一些比较私密的端口,所以安全性是比较高,但是开发与维护成本比较高;
B/S(Browser/Server、浏览器与服务器端):只开发一套服务器端的程序,而后利用浏览器作为客户端进行访问,这种开发与维护的成本较低(只有一套程序),但是由于其使用的是公共的HTTP协议并且使用的公共的80端口,所以其安全性相对较差,现在的开发基本上以“B/S”结构为主。
C/S程序模型,其分为两种开发:TCP(可靠的数据连接)、UDP(不可靠的数据连接);
TCP程序的基本实现
TCP的程序开发是网络程序的最基本的开发模型,其核心的特点是使用两个类实现数据的交互处理:ServerSocket(服务器端)、Socket(客户端)。
ServerSocket的主要目的是设置服务器的监听端口,而Socket需要指明要连接的服务器的地址与端口。
【范例】实现服务器端的定义
package 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("ByeBye........");
flag = false;
} else {
out.println("【ECHO】" + val);
// out.flush();//强制刷新缓冲区
}
}
}
scan.close();
out.close();
client.close();
server.close();
}
}
如果此时需要对程序进行测试,最好的方法是直接使用telnet命令完成,但此命令在window7之后变为了默认不开启状态,所以要想使用则必须单独启用此命令。
开启telnet方式: 控制面板——>程序——>启用或关闭windows功能,找到telnet client勾选确定即可。
在服务器端开启的情况下通过telnet指令输入:open localhost 9999
【范例】客户端代码
package demo.client;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
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;
}
}
}
public static String getString(String prompt) throws Exception {
System.out.println(prompt);
String str = KEYBOARD_INPUT.readLine();
return str;
}
}
此时就实现了一个最基础的客户端与服务器端之间的数据通讯操作。
多线程与网络编程
现在尽管已经实现了一个标准的网络程序开发,但是在整个的开发过程之中,本程序存在严重的性能缺陷,因为它只能够为一个线程提供Echo服务。如果现在的服务器需要有多人进行连接访问的时候那么其它的使用者将无法进行连接(等待连接)。
单线程的服务器开发本身就是一种不合理的做法,此时最好的解决方案将每一个连接到服务器上的客户端都通过一个线程对象来处理,即:服务器上启动多个线程,每一个线程单独为每一个客户端实现Echo服务支持。
范例:修改服务器端程序
package 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 IOException {
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("ByeBye........");
this.flag = false;//结束循环
} else {
out.println("【ECHO】" + val);
}
}
}
try {
scan.close();
out.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(9999);//设置服务器端的监听端口
System.out.println("等待客户端连接。。。。。。。。");
//1、首先要接收客户端发送来的信息,而后才可以将信息处理好发送回客户端
boolean flag = true;//循环标记
while(flag) {
Socket client = server.accept();//有客户端连接
new Thread(new ClientThread(client)).start();;
}
server.close();
}
}
如果在这类的代码里面追加一些集合的数据控制,实际上就可以实现一个80年代的聊天室了。
数据报发送与接收
TCP程序最大的特点是可靠的网络连接,但是在网络程序开发之中,也有UDP程序,基于数据报的 网络编程实现,如果要想实现UDP程序,需要两个类:DatagramPacket(数据内容)、DatagramSocket(网络发送与接收)。数据报就好比发送短消息一样,客户端是否接收到与发送者无关。
【范例】实现一个UDP客户端
package 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);//连接到999端口
byte data[] = new byte[1025];//接收消息
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 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);//连接到9999端口
String str = "www/asd/d";//要发送的消息内容
DatagramPacket packet = new DatagramPacket(str.getBytes(), 0,str.length(),InetAddress.getByName("localhost"),9999);
server.send(packet);
System.out.println("消息发送完毕。。。。");
server.close();
}
}
UDP发送的数据一定是不可靠的,但是TCP由于需要保证可靠的连接所以需要服务器的资源就越多了。
参考地址:
https://edu.aliyun.com/lesson_1012_9099