网络编程
---恢复内容开始---
网络:将地理上分开的主机连接在一起就形成了网络。但是在java中的网络编程不是已物理主机区分的。而是以不同的JVM进程来区分的
虽然JVM帮助用户隐藏了一些网络原始信息,但是有一些的操作还是留给了用户来实现了,例如:网络的连接分为两种:TCP(可靠的数据连接),UDP(不可靠的数据连接)。
从网络编程的本质来讲也分为两种类型:
C/S结构(客户/服务器):需要开发两套程序,一套是服务器端程序,另外一套是客户端程序。如果要更新,两套都要修改,开发复复杂度高,但是安全,稳定,因为可以自定义协议,自定义传输端口;
B/S结构(浏览器/服务器):通过浏览器来访问服务器端程序,开发者只需要开发服务器端代码即可,只需要维护一套程序,由于使用的是公共的协议,公开的端口,所以安全性较差。
开发一个基本的C/S程序
下面编写一个服务器端代码和一个客户端代码,主要是通过客户端去连接服务器端取得一些信息,服务器端只输出一次“HelloWorld”就表示操作结束。
如果要开发网络程序,使用java.net程序包即可,这个包里面有两个类:ServerSocket(服务器) Socket(客户端)。
范例:编写一个服务器端程序
1 package cn.Tony.netdemo.hello.server; 2 3 import java.io.PrintWriter; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 7 public class HelloServer { 8 public static void main(String[] args) throws Exception { 9 //1.创建一个服务器端的对象,所有的服务器一定要有一个监听端口 10 ServerSocket server=new ServerSocket(9999);//此时的服务在9999端口上等待连接 11 System.out.println("等客户来连接......"); 12 //2.需要等待客户连接,也就是说此时的程序在此处会进入到一个阻塞状态 13 Socket client=server.accept();//表示等待连接,连接的是客户 14 PrintWriter out=new PrintWriter(client.getOutputStream()); 15 out.println("Hello World!");//要有换行 16 out.close(); 17 server.close(); 18 } 19 }
范例:编写客户端
package cn.Tony.netdemo.hello.client; import java.net.Socket; import java.util.Scanner; public class HelloClient { public static void main(String[] args) throws Exception{ //1.表示连接到指定的服务器端的主机名称和端口。localhost=127.0.0.1 Socket client=new Socket("localhost",9999); //2.等待进行服务器端的输出,服务器端输出对客户端是输入4 Scanner scan=new Scanner(client.getInputStream()); scan.useDelimiter("\n"); if(scan.hasNext()) { System.out.println(scan.next()); } client.close(); } }
这个时候的服务器只能处理一次请求操作
ECHO程序
网络编程有一个最为经典的程序案例:ECHO程序的核心本质在于:将用户接受到的数据前面追加一个“ECHO”的头信息进行返回。这个程序要求用户可以一直进行信息的输入,一直到用户byebye才表示操作结束。
范例:编写服务器端
1 package cn.Tony.netdemo.echo; 2 3 import java.io.PrintStream; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 import java.util.Scanner; 7 8 public class EchoServer { 9 public static void main(String[] args) throws Exception{ 10 ServerSocket server=new ServerSocket(8888); 11 Socket client=server.accept(); 12 Scanner scan=new Scanner(client.getInputStream()); 13 scan.useDelimiter("\n"); 14 PrintStream out=new PrintStream(client.getOutputStream()); 15 boolean flag=true;//作为循环的处理标记 16 while(flag) {//相当于一直循环 17 if(scan.hasNext()) {//现在客户端有数据发送 18 String str=scan.next().trim();//防止有多与空格出现 19 if("byebye".equalsIgnoreCase(str)) {//操作结束 20 flag=false;//此时循环退出 21 break; 22 } 23 out.println("ECHO:"+str);//做出回应 24 } 25 } 26 out.close(); 27 scan.close(); 28 server.close(); 29 } 30 }
随后编写客户端,客户端肯定需要由用户输入数据,自己来输入数据就需要使用System.in并且结合Scanner进行操作,
范例:
1 package cn.Tony.netdemo.echo; 2 3 import java.io.PrintStream; 4 import java.net.Socket; 5 import java.util.Scanner; 6 7 8 public class EchoClient { 9 public static void main(String[] args)throws Exception { 10 Socket client=new Socket("localhost",8888); 11 Scanner keyScan=new Scanner(System.in); //用户输入 12 keyScan.useDelimiter("\n"); 13 Scanner netScan=new Scanner(client.getInputStream()); 14 netScan.useDelimiter("\n"); 15 PrintStream netOut=new PrintStream(client.getOutputStream()); 16 boolean flag=true; 17 while(flag) { 18 System.out.println("请输入要发送的信息:"); 19 if(keyScan.hasNext()) { 20 String str=keyScan.next(); 21 netOut.println(str);//发送给服务器端 22 if(netScan.hasNext()) {//服务器端有回应 23 System.out.println(netScan.next()); 24 } 25 if("byebye".equals(str)) { 26 flag=false; 27 break; 28 } 29 } 30 } 31 keyScan.close(); 32 netScan.close(); 33 client.close(); 34 } 35 }
以上完成一个基础的完成,但是程序有一个问题,本程序为单线程,因为所有的操作都在主方法里面进行的。
1 package cn.Tony.netdemo.echo; 2 3 import java.io.PrintStream; 4 import java.net.Socket; 5 import java.util.Scanner; 6 7 8 public class EchoClient { 9 public static void main(String[] args)throws Exception { 10 Socket client=new Socket("localhost",8888); 11 Scanner keyScan=new Scanner(System.in); //用户输入 12 keyScan.useDelimiter("\n"); 13 Scanner netScan=new Scanner(client.getInputStream()); 14 netScan.useDelimiter("\n"); 15 PrintStream netOut=new PrintStream(client.getOutputStream()); 16 boolean flag=true; 17 while(flag) { 18 System.out.println("请输入要发送的信息:"); 19 String str=null; 20 if(keyScan.hasNext()) { 21 str=keyScan.next().trim(); 22 netOut.println(str);//发送给服务器端 23 if(netScan.hasNext()) {//服务器端有回应 24 System.out.println(netScan.next()); 25 } 26 } 27 if("byebye".equalsIgnoreCase(str)) { 28 flag=false; 29 break; 30 } 31 } 32 keyScan.close(); 33 netScan.close(); 34 client.close(); 35 } 36 }
要想解决当前问题就必须进行多线程处理。把每一个连接都封装在一个线程里面,这样处理起来就可以不受到单线程局限了
1 package cn.Tony.netdemo.echo; 2 import java.io.PrintStream; 3 import java.net.ServerSocket; 4 import java.net.Socket; 5 import java.util.Scanner; 6 public class EchoServer { 7 private static class MyThread implements Runnable{ 8 private Socket client; 9 public MyThread(Socket client) { 10 this.client=client; 11 } 12 @Override 13 public void run() { 14 try { 15 Scanner scan=new Scanner(client.getInputStream()); 16 scan.useDelimiter("\n"); 17 PrintStream out=new PrintStream(client.getOutputStream()); 18 boolean flag=true;//作为循环的处理标记 19 while(flag) {//相当于一直循环 20 if(scan.hasNext()) {//现在客户端有数据发送 21 String str=scan.next().trim();//防止有多与空格出现 22 if("byebye".equalsIgnoreCase(str)) {//操作结束 23 out.println("BYEBYE!!!");//做出回应 24 flag=false;//此时循环退出 25 break; 26 } 27 out.println("ECHO:"+str);//做出回应 28 } 29 } 30 out.close(); 31 scan.close(); 32 }catch(Exception e) { 33 e.printStackTrace(); 34 } 35 } 36 } 37 public static void main(String[] args) throws Exception{ 38 ServerSocket server=new ServerSocket(8888); 39 boolean flag=true; 40 while(flag) { 41 Socket client=server.accept(); 42 new Thread(new MyThread(client)).start(); 43 } 44 server.close(); 45 } 46 }
实际上所有的服务器上,每一个用户的连接对服务器都是一个线程,线程通过指的就是对共享资源的控制
网络编程的时代结束了,现在也不再去搞任何编程,
---恢复内容结束---