网络连接
- 目的:实现不同主机间数据的共享,早期是主动共享,现在是被动共享
- OSI七层模型,TCP五层模型,所有数据在每一层上进行信息处理,最终通过物理设备(网卡)发送,网卡在整个网络中有唯一的Mac地址
- 网络编程:正对于TCP/UDP两种协议进行的网络开发,两个协议的细节组成非常繁琐,对网络协议的抽象性管理逻辑称为Socket编程
- Socket内部两种模式
- C/S(Client/Server):需要开发两套程序,一套服务器程序,一套客户端程序,在进行维护的时候服务器端和客户端的程序都需要更新(如QQ),使用特定的数据传输协议(用户自定义)及一些非公开端口,安全性较高
- B/S(Browser/Server):基于浏览器实现客户端应用,只需要开发服务器端的程序,如需进行维护只需修改服务器代码,维护与开发成本降低,使用公共的HTTP协议(基于TCP协议的实现),所以安全性较差
- 网络编程属于C/S模型,Web开发属于B/S模型
- C/S模型分为
- TCP程序:三次握手,四次挥手
- UDP程序:发送数据报,接收数据报的一方不一定可以接收到数据(发短信,手机关机)
网络程序
- java.net包完成,提供两个类
- java.net.ServerSocket:工作在服务器端的程序类,定义服务器监听端口,及接收客户端请求
- java.net.Socket:每一个客户端都使用一个Socket的概念进行描述
- 端口是进入服务的“门”,每个端口只能绑定一个服务,建议用5000以上
- 连接的目的是进行IO通讯
服务器端程序
- 验证服务器是否可正常使用,可利用操作系统中的telnet命令
- 传输字节数据,用PrintStream
- win+R > Telnet > open localhost 9999
- telnet 仅是基础测试环境,不是客户端
- 服务器端数据的输出,就是客户端数据的输入
- accept():打开服务器监听
1 import java.io.PrintStream; 2 import java.net.ServerSocket; 3 import java.net.Socket; 4 5 public class HelloServer { 6 public static void main(String[] args) throws Exception{ 7 ServerSocket serversocket = new ServerSocket(9999); // 本程序在9999端口连接 8 System.out.println("[HelloServer] 服务器端启动监听... ..."); 9 // 每一个连接到服务器端的客户端都通过Socket对象来描述,所以要等待连接 10 Socket client = serversocket.accept(); // 等待客户端连接 11 // 向指定的Socket实现数据的输出,获取Socket的输出流 12 PrintStream out = new PrintStream(client.getOutputStream()); // 通过打印流输出 13 out.println("www.yootk.com"); // 输出信息 14 client.shutdownOutput(); // 数据发送完毕后再关闭 15 serversocket.close(); // 关闭整个服务器 16 } 17 }
客户端程序
- Socket类描述的就属于客户端信息,在客户端主要用它来实现服务器端的连接,而服务器端每个连接到服务器的客户都通过Socket来描述
1 import java.net.Socket; 2 import java.util.Scanner; 3 4 public class HelloClient { 5 public static void main(String[] args) throws Exception{ 6 // Socket是工作在客户端的程序类,每一个Socket对象描述的都是一个独立的客户端 7 Socket client = new Socket("localhost",9999); 8 Scanner scanner = new Scanner(client.getInputStream()); // 服务器端的输出为客户端的输入 9 if(scanner.hasNext()) { // 此时有数据需要进行输出 10 System.out.println("[HelloClient] "+scanner.next()); 11 } 12 client.shutdownInput(); 13 } 14 }
Echo模型
- 网络通讯程序的核心接口
- 客户端发送数据给服务器端,服务器端接收到数据后直接进行回应
- 回应处理可持续进行,当客户端确认不再继续交互时断开
- Socket可获取InputStream、OutputStream
- 实现最为核心的网络接口交互模型,再继续扩展可实现更加丰富内容的传输
服务器端程序
1 import java.io.PrintStream; 2 import java.net.ServerSocket; 3 import java.net.Socket; 4 import java.util.Scanner; 5 6 public class EchoServer { 7 public static void main(String[] args) throws Exception{ 8 ServerSocket serversocket = new ServerSocket(9999); 9 Socket client = serversocket.accept(); // 进入阻塞状态,等待客户端连接 10 Scanner scanner = new Scanner(client.getInputStream()); // 服务器端输入流 11 PrintStream out = new PrintStream(client.getOutputStream()); // 服务器端输出流 12 boolean flag = true; 13 while(flag) { 14 if(scanner.hasNext()) { // 如果现在有输入的数据内容 15 String value = scanner.next().trim(); 16 if(value.equalsIgnoreCase("exit")) { 17 out.println("[EchoServer]exit"); 18 flag = false; 19 break; 20 } 21 out.println("ECHO:"+ value); 22 } 23 } 24 serversocket.close(); 25 } 26 }
客户端程序
1 import java.io.PrintStream; 2 import java.net.Socket; 3 import java.util.Scanner; 4 5 public class EchoClient { 6 public static void main(String[] args) throws Exception{ 7 Socket client = new Socket("localhost",9999); 8 Scanner scanner = new Scanner(client.getInputStream()); // 客户端输入流 9 PrintStream out = new PrintStream(client.getOutputStream()); // 客户端输出流 10 boolean flag = true; 11 while(flag) { 12 String value = KeyboardInput.getString("请输入数据:"); 13 out.println(value); 14 if(scanner.hasNext()) { 15 System.out.println("[ECHO客户端]" + scanner.next()); //服务器端响应 16 } 17 if(value.equalsIgnoreCase("exit")){ 18 flag = false; 19 } 20 } 21 client.close(); 22 } 23 }
BIO处理模型
- BIO(Blocking IO、阻塞IO处理)是最为传统的一种网络通讯模型的统一描述,解决服务器的并发处理问题,ECHO模型属于单线程服务器的开发,即同一个时间段只能有一个线程进行访问
- 创建新的线程处理类,包裹Socket对象
- 问题:需要先连接,然后服务器分配线程对象,若此时客户端即使没有与服务器端产生任何交互,那么这个线程的连接也要维持着,面对高并发访问程序会导致性能浪费,为解决次问题创建了NIO模型
服务器端
1 import java.net.ServerSocket; 2 import java.net.Socket; 3 4 public class EchoServer { 5 public static void main(String[] args) throws Exception{ 6 ServerSocket serversocket = new ServerSocket(9999); 7 boolean flag = true; 8 while(flag) { 9 Socket client = serversocket.accept(); 10 new Thread(new EchoHandle(client)).start(); 11 } 12 serversocket.close(); 13 } 14 }
服务器端线程类
1 import java.io.PrintStream; 2 import java.net.Socket; 3 import java.util.Scanner; 4 5 public class EchoHandle implements Runnable{ 6 private Socket client; 7 public EchoHandle(Socket client) { 8 this.client = client; 9 } 10 @Override 11 public void run() { 12 try { 13 Scanner scanner = new Scanner(client.getInputStream()); // 服务器端输入流 14 PrintStream out = new PrintStream(client.getOutputStream()); // 服务器端输出流 15 boolean flag = true; 16 while(flag) { 17 if(scanner.hasNext()) { // 如果现在有输入的数据内容 18 String value = scanner.next().trim(); 19 if(value.equalsIgnoreCase("exit")) { 20 out.println("[EchoServer]exit"); 21 flag = false; 22 }else { 23 out.println("ECHO:"+ value); 24 } 25 } 26 } 27 scanner.close(); 28 out.close(); 29 client.close(); 30 }catch(Exception e) {} 31 } 32 }
客户端
1 import java.io.PrintStream; 2 import java.net.Socket; 3 import java.util.Scanner; 4 5 public class EchoClient { 6 public static void main(String[] args) throws Exception{ 7 Socket client = new Socket("localhost",9999); 8 Scanner scanner = new Scanner(client.getInputStream()); // 客户端输入流 9 PrintStream out = new PrintStream(client.getOutputStream()); // 客户端输出流 10 boolean flag = true; 11 while(flag) { 12 String value = KeyboardInput.getString("请输入数据:"); 13 out.println(value); 14 if(scanner.hasNext()) { 15 System.out.println("[ECHO客户端]" + scanner.next()); //服务器端响应 16 } 17 if(value.equalsIgnoreCase("exit")){ 18 flag = false; 19 } 20 } 21 scanner.close(); 22 out.close(); 23 client.close(); 24 } 25 }
UDP程序
- TCP:可靠连接模式,可保证接收稳定性,但会带来严重的性能损耗
- UDP:不保证用户一定可以收到,但性能较高
- 类比:发传单
- 实例:手机短信(不开机收不到)
- UDP客户端:等待服务器端进行消息数据的发送,如接收到内容则继续向下执行,进行具体内容输出
- HTTP协议里可能将TCP协议更换
- 用Netty开发更加简单
服务器端
1 import java.net.DatagramPacket; 2 import java.net.DatagramSocket; 3 import java.net.InetAddress; 4 5 public class UDPServer { 6 public static void main(String[] args) throws Exception{ 7 DatagramSocket server = new DatagramSocket(8000); // 服务器端端口 8 String message = "www.yootk.com"; 9 DatagramPacket packet = new DatagramPacket(message.getBytes(),0,message.length(),InetAddress.getByName("localhost"),9999); 10 server.send(packet); // 发送数据报 11 System.out.println("[UDPServer]finished..."); 12 server.close(); 13 } 14 }
客户端
1 import java.net.DatagramPacket; 2 import java.net.DatagramSocket; 3 4 public class UDPClient { 5 public static void main(String[] args) throws Exception{ 6 DatagramSocket client = new DatagramSocket(9999); 7 byte data[] = new byte[1024]; 8 DatagramPacket packet = new DatagramPacket(data, data.length); 9 System.out.println("[UDPClient]wait..."); 10 client.receive(packet); 11 System.out.println("[UDPClient]"+ new String(data,0,packet.getLength())); 12 } 13 }