网络编程那点事
1 什么是网络编程
网络编程的本质就是两个设备之间的数据交换,当然在计算机网络中,设备主要指计算机。数据传递本身没有多大的难度,就是把一个设备中的数据发送给另外一个设备。
现在的网络编程基本上都是基于请求 / 响应方式的,也就是一个设备发送请求数据给另外一个,然后接受另一个设备的反馈。
在网络编程中,发起连接程序,也就是发送第一次请求的程序,被称为为客户端(Client)等待其他程序连接的程序称作服务器(Server)客户端程序可以在需要的时候启动,而服务器为了能够时刻响应连接,则需要一直启动。
总结:通过编码的方式让不同计算机之间通过网路相互通信(传递数据)
2 网络编程要解决的核心问题
a. 寻址:使用 IP + 端口
b. 协议:数据的传输规则 / 方式
3 常见的协议有哪些
a. UDP 协议:面向非连接的协议,传递数据时不关心连接状态直接发送,他的传输效率高,传递的数据不安全。
b. TCP 协议:面向连接的协议,传递数据时关系连接的状态,连接成功才会发送数据,传递效率相对于UDP协议要低,会通过三次握手机制保证数据的完整性。
c. HTTP 协议:属于应用层协议,层面比较高,HTTP 协议底层还是通过 TCP 协议传输。
4 网络中的OSI模型
OSI是Open System Interconnection的缩写,意为开放式系统互联. OSI 七层模型通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯,因此其最主要的功能就是帮助不同类型的主机实现数据传输 注意: 所处的层越低传输效率越高
5 常见的网络编程
C/S 访问某个服务器端的时候,需要特定的客户端;
B/S 浏览器,是一个公共的客户端,通过浏览器可以给不同的服务器发送数据。
客户端:
a. C/S 模式中对应的客户端;
b. B/S 中的浏览器,通用的客户端;
c. 通过代码实现:http-client(模拟浏览器)
d. 自己编码实现一个客户端;
服务端:
自己编码实现服务端。
6 Java 中网络编程的开发思路
Java中实现网络编程,也叫Socket编程,通过Java实现网络编程主要有一下几种方式:
a. Java BIO:基于Java阻塞IO实现网络编程(Block IO 同步阻塞IO)
b. Java NIO:基于Java的同步非阻塞IO实现网络编程(同步非阻塞IO)
7. 网络编程的实现
7.1 网络编程开发服务器端
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * 创建serverScoket对象 服务端对象 */ public class TestServerSocket { public static void main(String[] args) throws IOException { //创建一个服务器 ServerSocket serverSocket = new ServerSocket(8989); System.out.println("服务器已经启动........"); while(true) { //让服务器接受|接收客户端 Socket socket = serverSocket.accept(); //处理请求数据 InputStream inputStream = socket.getInputStream(); StringBuilder builder = new StringBuilder(); int len = 0; byte[] b = new byte[1024]; while (true) { len = inputStream.read(b); if (len == -1) break; builder.append(new String(b, 0, len)); } System.out.println(builder.toString()); socket.shutdownInput();//获取请求数据 结束 //处理业务 //服务端响应客户端数据 OutputStream outputStream = socket.getOutputStream(); outputStream.write("讲".getBytes()); socket.shutdownOutput(); } } }
7.2 网络编程开发客户端
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; //创建客户端对象 public class TestSocket { public static void main(String[] args) throws IOException { //创建socket对象 Socket socket = new Socket("192.168.0.3", 8989); //向服务器发送数据 OutputStream outputStream = socket.getOutputStream(); outputStream.write("hello Server".getBytes()); //代表客户端发送的数据完成 socket.shutdownOutput(); //获取服务器端响应数据 InputStream inputStream = socket.getInputStream(); int len = 0; byte[] b = new byte[1024]; StringBuilder builder = new StringBuilder(); while(true){ len = inputStream.read(b); if(len==-1) break; builder.append(new String(b,0,len)); } System.out.println(builder.toString()); socket.shutdownInput();//确定读取响应数据结束 } }
8 网络编程服务端支持多客户端访问
8.1 客户端 TestSocket01
import java.io.*; import java.net.Socket; //创建一个客户端对象 public class TestSocket001 { public static void main(String[] args) throws IOException { //创建客户端对象 Socket socket = new Socket("192.168.0.3",8989); //发送数据 OutputStream outputStream = socket.getOutputStream(); //封装过滤流 DataOutputStream dataOutputStream = new DataOutputStream(outputStream); dataOutputStream.writeUTF("abc"); dataOutputStream.flush(); socket.shutdownOutput();//请求数据发送完成 //处理响应结果 InputStream inputStream = socket.getInputStream(); //封装过滤流 DataInputStream dataInputStream = new DataInputStream(inputStream); System.out.println(dataInputStream.readUTF()); socket.shutdownInput();//明确client处理响应完毕 } }
8.2 客户端 TestSocket002
import java.io.*; import java.net.Socket; //创建一个客户端对象 public class TestSocket002 { public static void main(String[] args) throws IOException { for (int i = 0; i <100 ; i++) { //创建客户端对象 Socket socket = new Socket("192.168.0.3",8989); //发送数据 OutputStream outputStream = socket.getOutputStream(); //封装过滤流 DataOutputStream dataOutputStream = new DataOutputStream(outputStream); dataOutputStream.writeUTF("aaa"); dataOutputStream.flush(); socket.shutdownOutput();//请求数据发送完成 //处理响应结果 InputStream inputStream = socket.getInputStream(); //封装过滤流 DataInputStream dataInputStream = new DataInputStream(inputStream); System.out.println(dataInputStream.readUTF()); socket.shutdownInput();//明确client处理响应完毕 } } }
8.3 客户端 TestSocket3
import java.io.*; import java.net.Socket; //创建一个客户端对象 public class TestSocket003 { public static void main(String[] args) throws IOException { //创建客户端对象 Socket socket = new Socket("192.168.0.3",8989); //发送数据 OutputStream outputStream = socket.getOutputStream(); //封装过滤流 DataOutputStream dataOutputStream = new DataOutputStream(outputStream); dataOutputStream.writeUTF("ccc"); dataOutputStream.flush(); socket.shutdownOutput();//请求数据发送完成 //处理响应结果 InputStream inputStream = socket.getInputStream(); //封装过滤流 DataInputStream dataInputStream = new DataInputStream(inputStream); System.out.println(dataInputStream.readUTF()); socket.shutdownInput();//明确client处理响应完毕 } }
8.4 单线程服务端【阻塞式响应多个客户端】
import java.io.*; import java.net.ServerSocket; import java.net.Socket; //创建服务器端 public class TestServerSocket001 { //操作 进程 开启多线程 进程数量有限的 系统上线 cpu 1 100000000 1 public static void main(String[] args) throws IOException, InterruptedException { //创建serverSocket对象 ServerSocket serverSocket = new ServerSocket(8989); System.out.println("服务器已经启动......"); while (true){ //接收客户端请求 每一个请求创建一个线程 Socket socket = serverSocket.accept(); //处理请求数据 InputStream inputStream = socket.getInputStream(); //封装过滤流 DataInputStream dataInputStream = new DataInputStream(inputStream); String readUTF = dataInputStream.readUTF(); System.out.println(readUTF +" 线程名称:"+Thread.currentThread().getName()); socket.shutdownInput();//明确处理请求数据完毕 //处理业务 if(readUTF.equals("abc")){ Thread.sleep(10000); } //响应客户端对象 OutputStream outputStream = socket.getOutputStream(); //封装过滤流 DataOutputStream dataOutputStream = new DataOutputStream(outputStream); dataOutputStream.writeUTF("我是server,我已经将你发送的请求处理完毕,没事别来找我....."); socket.shutdownOutput();//明确服务器响应完毕 } } }
注意:这种方式采用单一主线程阻塞式响应多个客户端请求,主线程只能同步响应,同步阻塞;
9 服务端的多线程操作
9.1 开启多线程的方式
a 实现 Runnable 接口,重写 run() 方法;
b. 继承 Thread 类,覆盖 run() 方法;
c. 线程池 ThreadPoolExecuter 使用;参考文档:Java并发编程-线程池的认识(一) Java并发编程-线程池的认识(二)
9.2 创建 Thread 对象,使用匿名方法实现多线程
import java.io.*; import java.net.ServerSocket; import java.net.Socket; //创建服务器端 public class TestServerSocket001 { //操作 进程 开启多线程 进程数量有限的 系统上线 cpu 1 100000000 1 public static void main(String[] args) throws IOException, InterruptedException { //创建serverSocket对象 ServerSocket serverSocket = new ServerSocket(8989); System.out.println("服务器已经启动......"); while (true){ //接收客户端请求 每一个请求创建一个线程 final Socket socket = serverSocket.accept(); new Thread(new Runnable() { public void run() { try { //处理请求数据 InputStream inputStream = socket.getInputStream(); //封装过滤流 DataInputStream dataInputStream = new DataInputStream(inputStream); String readUTF = dataInputStream.readUTF(); System.out.println(readUTF +" 线程名称:"+Thread.currentThread().getName()); socket.shutdownInput();//明确处理请求数据完毕 //处理业务 if(readUTF.equals("abc")){ Thread.sleep(10000); } //响应客户端对象 OutputStream outputStream = socket.getOutputStream(); //封装过滤流 DataOutputStream dataOutputStream = new DataOutputStream(outputStream); dataOutputStream.writeUTF("我是server,我已经将你发送的请求处理完毕,没事别来找我....."); socket.shutdownOutput();//明确服务器响应完毕 } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } }
9.3 使用线程池实现服务端多线程
import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //创建服务器端 public class TestServerSocket002 { //操作 进程 开启多线程 进程数量有限的 系统上线 cpu 1 100000000 1 public static void main(String[] args) throws IOException, InterruptedException { //创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(10); //创建serverSocket对象 ServerSocket serverSocket = new ServerSocket(8989); System.out.println("服务器已经启动......"); while (true){ //接收客户端请求 每一个请求创建一个线程 final Socket socket = serverSocket.accept(); executorService.submit(new Runnable() { public void run() { try { //处理请求数据 InputStream inputStream = socket.getInputStream(); //封装过滤流 DataInputStream dataInputStream = new DataInputStream(inputStream); String readUTF = dataInputStream.readUTF(); System.out.println(readUTF +" 线程名称:"+Thread.currentThread().getName()); socket.shutdownInput();//明确处理请求数据完毕 //处理业务 if(readUTF.equals("abc")){ Thread.sleep(10000); } //响应客户端对象 OutputStream outputStream = socket.getOutputStream(); //封装过滤流 DataOutputStream dataOutputStream = new DataOutputStream(outputStream); dataOutputStream.writeUTF("我是server,我已经将你发送的请求处理完毕,没事别来找我....."); socket.shutdownOutput();//明确服务器响应完毕 } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } }
9.4 线程池基本使用
import java.util.concurrent.*; public class TestThreadPool { public static void main(String[] args) { //创建线程池对象 //参数1: 线程池中核心线程数量 //参数2: 线程池中最大线程数 //参数3: 线程池中线程的空闲时间 //参数4: 决定线程中线程的空闲时间的单位 TimeUnit //参数5: 线程等待队列 //参数6: 创建线程工厂 //参数7: 用于被拒绝任务的处理程序 // 策略一: 直接拒绝 new ThreadPoolExecutor.AbortPolicy() RejectedExecutionHandler 线程拒绝处理器 // 策略二: 在调用execute方法的程序中执行该任务 new ThreadPoolExecutor.CallerRunsPolicy() 如果程序已经结束则丢弃任务 // 策略三: new ThreadPoolExecutor.DiscardOldestPolicy() 丢弃等待队列中的请求,然后重试 execute ,除非执行程序被关闭,在这种情况下,任务被丢弃。 // 策略四: new ThreadPoolExecutor.DiscardPolicy() 丢弃最后一次无法执行的任务 静默丢弃 ArrayBlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(1); ThreadFactory threadFactory = Executors.defaultThreadFactory(); ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 1, //核心线程池数量 银行窗口 3 2, //线程池最大线程数量 临时窗口 5-3 10, //空闲10 TimeUnit.SECONDS, //单位 秒 workQueue, //线程等待队列 银行中小板凳 2 threadFactory, //创建新线程工厂 new ThreadPoolExecutor.DiscardPolicy()); //拒绝处理器 //调用线程池对象中execute方法执行线程任务 /* for (int i = 1; i <=8 ; i++) { threadPoolExecutor.execute(new MyRunnable()); }*/ threadPoolExecutor.execute(new Runnable() { public void run() { System.out.println("任务1 "+Thread.currentThread().getName()); } }); threadPoolExecutor.execute(new Runnable() { public void run() { System.out.println("任务2 "+Thread.currentThread().getName()); } }); threadPoolExecutor.execute(new Runnable() { public void run() { System.out.println("任务3 "+Thread.currentThread().getName()); } }); threadPoolExecutor.execute(new Runnable() { public void run() { System.out.println("任务4 "+Thread.currentThread().getName()); } }); } } class MyRunnable implements Runnable{ public void run() { System.out.println("任务.......线程名称:"+Thread.currentThread().getName()); } }
多线程编码掌握基础并不难,清楚原理和执行流程,实践才能更好的理解多线程!加油......