JavaSE预习Day4
网络编程
引入
基本的通信架构有2种形式:CS架构( Client客户端/Server服务端 ) 、 BS架构(Browser浏览器/Server服务端)(Browser浏览器不需要程序员开发实现。)。无论是CS架构,还是BS架构的软件都必须依赖网络编程!
网络通信三要素
IP
IP:设备在网络中的地址,
是唯一的标识。
IP地址有两种形式:IPv4(32bit,点分十进制表示法)、IPv6(128bit,分成8段表示,每段每四位编码成一个十六进制位表示, 数之间用冒号(:)分开)
IP域名–>IP(DNS服务器)
公网IP, 内网IP
公网IP:是可以连接互联网的IP地址;内网IP:也叫局域网IP,只能组织机构内部使用。
192.168. 开头的就是常见的局域网地址,范围即为192.168.0.0–192.168.255.255,专门为组织机构
内部使用。
特殊IP地址:
127.0.0.1、localhost:代表本机IP,只会寻找当前所在的主
机。
IP常用命令:
ipconfig:查看本机IP地址。
ping IP地址:检查网络是否连通。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class InetAddressTest { public static void main(String[] args) throws IOException { //1.取本机IP,会以一个inetAddress的对象返回 InetAddress byAddress = InetAddress.getLocalHost(); System.out.println(byAddress.getHostName());//取该ip地址对象对应的主机名。 System.out.println(byAddress.getHostAddress());//获取该ip地址对象中的ip地址信息。 //2.指定ip地址或者域名,返回一个inetAddress对象 InetAddress byName = InetAddress.getByName("www.baidu.com"); System.out.println(byName.getHostName()); System.out.println(byName.getHostAddress()); //3.在指定毫秒内,判断主机与该ip对应的主机是否能连通 System.out.println(byName.isReachable(6000)); } }
|
端口
端口:应用程序在设备中唯一的标识,标记正在计算机设备上运行的应用程序的,被规定为一个 16 位的二进制,范围是 065535。
分类
周知端口:01023,被预先定义的知名应用占用(如:HTTP占用 80,FTP占用21)
注册端口:1024~49151,分配给用户进程或某些应用程序。
动态端口:49152到65535,之所以称为动态端口,是因为它 一般不固定分配某种进程,而是动态分配。
注意:我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出
错。
协议
协议:连接和数据在网络中传输的规则。



UDP通信
UDP通信快速入门

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| public class Client { /* 客户端实现步骤 ① 创建DatagramSocket对象(客户端对象) 扔韭菜的人 ② 创建DatagramPacket对象封装需要发送的数据(数据包对象) 韭菜 盘子 ③ 使用DatagramSocket对象的send方法,传入DatagramPacket对象 开始抛出韭菜 ④ 释放资源 */ public static void main(String[] args) throws IOException { //1.创建客户端对象(扔韭菜盒子的人) DatagramSocket datagramSocket = new DatagramSocket();//创建客户端的Socket对象,系统会随机分配一个端口号。 //也可人为设置端口 //2.创建数据包对象封装要发出去的数据(韭菜盒子) byte buf[]="一得阁拉米".getBytes(); DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), 6666); //3.正式发送 datagramSocket.send(datagramPacket); System.out.println("客户端数据发送完毕"); datagramSocket.close(); } } public class Server { /* 服务端实现步骤 ① 创建DatagramSocket对象并指定端口(服务端对象) 接韭菜的人 ② 创建DatagramPacket对象接收数据(数据包对象) 韭菜盘子 ③ 使用DatagramSocket对象的receive方法,传入DatagramPacket对象 开始接收韭菜 ④ 释放资源 */ //先启动服务端 public static void main(String[] args) throws IOException { System.out.println("服务端启动完毕"); //1.创建服务端对象(接住韭菜盒子的人) DatagramSocket datagramSocket2 = new DatagramSocket(6666);//创建服务端的Socket对象,并指定端口号 //2.创建数据包对象接受数据(韭菜盒子) byte buffer[]=new byte[1024*64];//64kb DatagramPacket datagramPacket2 = new DatagramPacket(buffer, buffer.length); //3.正式接收 datagramSocket2.receive(datagramPacket2); System.out.println("服务端数据接受完毕"); int length=datagramPacket2.getLength(); String s = new String(buffer,0,length); System.out.println(s); //补充:找到客户端ip,名字和端口 System.out.println(datagramPacket2.getAddress().getHostName()); System.out.println(datagramPacket2.getAddress().getHostAddress()); System.out.println(datagramPacket2.getPort()); datagramSocket2.close(); } }
|
UDP通信多发多收(多个客户端发)

注:这样服务端不要人为设置端口。不然会冲突
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| public class Client { /* 客户端反复发送 */ public static void main(String[] args) throws IOException { DatagramSocket datagramSocket = new DatagramSocket();//创建客户端的Socket对象,系统会随机分配一个端口号。 //也可人为设置端口 Scanner sc=new Scanner(System.in); while (true) { String s = sc.nextLine(); byte buf[]=s.getBytes(); if(s.equals("exit")){ System.out.println("退出成功"); datagramSocket.close(); break;//跳出死循环 } DatagramPacket datagramPacket = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), 6666); //3.正式发送 datagramSocket.send(datagramPacket); } } } public class Server { /* 服务端反复接收,不要关闭服务端 //先启动服务端 */ public static void main(String[] args) throws IOException { System.out.println("服务端启动完毕"); //1.创建服务端对象(接住韭菜盒子的人) DatagramSocket datagramSocket2 = new DatagramSocket(6666);//创建服务端的Socket对象,并指定端口号 //2.创建数据包对象接受数据(韭菜盒子) byte buffer[]=new byte[1024*64];//64kb DatagramPacket datagramPacket2 = new DatagramPacket(buffer, buffer.length); while (true) { //3.正式接收 datagramSocket2.receive(datagramPacket2); System.out.println("服务端数据接受完毕"); int length=datagramPacket2.getLength(); String s = new String(buffer,0,length); System.out.println(s); //补充:找到客户端ip,名字和端口 System.out.println(datagramPacket2.getAddress().getHostName()); System.out.println(datagramPacket2.getAddress().getHostAddress()); System.out.println(datagramPacket2.getPort()); System.out.println("-------"); }
} }
|
TCP通信
TCP通信-快速入门

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import java.io.*; import java.net.Socket; /* 客户端实现步骤 ① 创建客户端的Socket对象,请求与服务端的连接。 ② 使用socket对象调用getOutputStream()方法得到字节输出流。 ③ 使用字节输出流完成数据的发送。 ④ 释放资源:关闭socket管道。 */ public class Client { //客户端开发 public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1 ",8888);//1.根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket OutputStream os= socket.getOutputStream();//2.字节输出流 DataOutputStream dos=new DataOutputStream(os);//3.高级流 dos.writeUTF("DelayNoMore");//4.开始写 dos.close();//管高级流低级流也会关 socket.close(); } } import java.io.DataInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket;
public class Server { //服务端开发 /* 服务端实现步骤 ① 创建ServerSocket对象,注册服务端端口。 ② 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。 ③ 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。 ④ 释放资源:关闭socket管道 */ public static void main(String[] args) throws IOException { System.out.println("服务端启动完毕"); ServerSocket ss=new ServerSocket(8888); Socket socket = ss.accept();//阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象 InputStream is = socket.getInputStream(); DataInputStream dis=new DataInputStream(is); System.out.println(dis.readUTF()); //补充:找到客户端ip,名字和端口 System.out.println(socket.getInetAddress().getHostAddress()); System.out.println(socket.getInetAddress().getHostName()); System.out.println(socket.getPort()); dis.close(); ss.close(); } }
|
TCP通信-多发多收
使用TCP通信实现:多发多收消息。
① 客户端使用死循环,让用户不断输入消息。
② 服务端也使用死循环,控制服务端收完消息,继续等待接收下一个消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class Client { //客户端开发 public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1",8888);//1.根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket OutputStream os= socket.getOutputStream();//2.字节输出流 DataOutputStream dos=new DataOutputStream(os);//3.高级流 Scanner sc=new Scanner(System.in); while (true) { System.out.println("请发送:"); String s = sc.nextLine(); if(s.equals("exit")){ System.out.println("退出成功"); dos.close(); socket.close(); break;//跳出死循环 } dos.writeUTF(s);//4.开始写 dos.flush(); } dos.close();//管高级流低级流也会关 socket.close(); } } public class Server { //服务端开发 public static void main(String[] args) throws IOException { System.out.println("服务端启动完毕"); ServerSocket ss=new ServerSocket(8888); Socket socket = ss.accept();//阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象 InputStream is = socket.getInputStream(); DataInputStream dis=new DataInputStream(is); while (true) { String s = null;//如果客户端关闭了服务端会报异常,直接捕获 try { s = dis.readUTF(); } catch (IOException e) { System.out.println(socket.getInetAddress().getHostName()+"离线了"); dis.close(); break; } System.out.println(s); } } }
|
TCP通信-支持与多个客户端同时通信
目前我们开发的服务端程序,是否可以支持与多个客户端同时通信 ?
不可以的。
因为服务端现在只有一个主线程,只能处理一个客户端的消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| public class Client { //客户端开发 public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1",8888);//1.根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket OutputStream os= socket.getOutputStream();//2.字节输出流 DataOutputStream dos=new DataOutputStream(os);//3.高级流 Scanner sc=new Scanner(System.in); while (true) { System.out.println("请发送:"); String s = sc.nextLine(); if(s.equals("exit")){ System.out.println("退出成功"); dos.close(); socket.close(); break;//跳出死循环 } dos.writeUTF(s);//4.开始写 dos.flush(); } dos.close();//管高级流低级流也会关 socket.close(); } } public class Server { //服务端开发 public static void main(String[] args) throws IOException { System.out.println("服务端启动完毕"); ServerSocket ss=new ServerSocket(8888); while (true) { Socket socket = ss.accept();//阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象 System.out.println("有人上线了"+socket.getRemoteSocketAddress()); new MyThread(socket).start(); } } } public class MyThread extends Thread{ private Socket socket; public MyThread(Socket socket){ this.socket=socket; } @Override public void run(){ try { InputStream is = socket.getInputStream(); DataInputStream dis=new DataInputStream(is); while (true) { try { System.out.println(dis.readUTF()); } catch (IOException e) { System.out.println("有人下线了"+socket.getRemoteSocketAddress()); dis.close(); socket.close(); break; } } } catch (IOException e) { e.printStackTrace(); } } }
|
TCP通信-综合案例1群聊
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
| public class Client { //客户端开发 public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1",8888);//1.根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket new MyClientThread(socket).start();// 创建一个独立的线程,负责随机从socket中接收服务端发送过来的消息。 OutputStream os= socket.getOutputStream();//2.字节输出流 DataOutputStream dos=new DataOutputStream(os);//3.高级流 Scanner sc=new Scanner(System.in); while (true) { System.out.println("请发送:"); String s = sc.nextLine(); if(s.equals("exit")){ System.out.println("退出成功"); dos.close(); socket.close(); break;//跳出死循环 } dos.writeUTF(s);//4.开始写 dos.flush(); } // dos.close();//管高级流低级流也会关 // socket.close(); } } public class MyClientThread extends Thread{ private Socket socket; public MyClientThread(Socket socket) { this.socket=socket; }
@Override public void run() { try { InputStream is = socket.getInputStream(); DataInputStream dis = new DataInputStream(is);//接收服务端传回来的消息 while (true){ try { String msg = dis.readUTF(); System.out.println(msg); } catch (Exception e) { System.out.println("自己下线了:" + socket.getRemoteSocketAddress()); dis.close(); socket.close(); break; } } } catch (Exception e) { e.printStackTrace(); } } } public class Server { public static List<Socket>list=new ArrayList<>(); //服务端开发 /* 服务端实现步骤 ① 创建ServerSocket对象,注册服务端端口。 ② 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。 ③ 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。 ④ 释放资源:关闭socket管道 */ public static void main(String[] args) throws IOException { System.out.println("服务端启动完毕"); ServerSocket ss=new ServerSocket(8888); while (true) { Socket socket = ss.accept();//阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象 list.add(socket); System.out.println("有人上线了"+socket.getRemoteSocketAddress()); new MyServerThread(socket).start(); } } } public class MyServerThread extends Thread{ private Socket socket; public MyServerThread(Socket socket){ this.socket=socket; } @Override public void run(){ try { InputStream is = socket.getInputStream(); DataInputStream dis=new DataInputStream(is); while (true) { try { String s=dis.readUTF(); System.out.println(s); sendMsg(s);//把这个消息分发给全部客户端进行接收。 } catch (IOException e) { System.out.println("有人下线了"+socket.getRemoteSocketAddress()); Server.list.remove(socket); dis.close(); socket.close(); break; } } } catch (IOException e) { e.printStackTrace(); } } public void sendMsg(String s) throws IOException { for (Socket socket1 : Server.list) { //OutputStream os= socket.getOutputStream();//这一行写错了,如果这样的话只有发送的客户端收到消息! OutputStream os= socket1.getOutputStream(); DataOutputStream dos=new DataOutputStream(os); dos.writeUTF(s); dos.flush(); } } }
|
TCP通信-综合案例2 BS架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class Server { public static List<Socket>list=new ArrayList<>(); //服务端开发 /* 服务端实现步骤 ① 创建ServerSocket对象,注册服务端端口。 ② 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。 ③ 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。 ④ 释放资源:关闭socket管道 */ public static void main(String[] args) throws IOException { System.out.println("服务端启动完毕"); ServerSocket ss=new ServerSocket(8888); while (true) { Socket socket = ss.accept();//阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象 list.add(socket); System.out.println("有人上线了"+socket.getRemoteSocketAddress()); new MyServerThread(socket).start(); } } } public class MyServerThread extends Thread{ private Socket socket; public MyServerThread(Socket socket){ this.socket=socket; } @Override public void run(){ try { OutputStream os = socket.getOutputStream(); PrintStream ps=new PrintStream(os); ps.println("HTTP/1.1 200 OK"); ps.println("Content-Type:text/html;charset=UTF-8"); ps.println(); // 必须换行 ps.println("<div style='color:red;font-size:120px;text-align:center'>黑马程序员666<div>"); ps.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); }
} }
|
拓展:使用线程池来优化(而非多个线程,节省性能)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class Server { public static List<Socket>list=new ArrayList<>(); //服务端开发 /* 服务端实现步骤 ① 创建ServerSocket对象,注册服务端端口。 ② 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。 ③ 通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。 ④ 释放资源:关闭socket管道 */ public static void main(String[] args) throws IOException { ExecutorService ex=new ThreadPoolExecutor(32,32,0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(8),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());//实现类 System.out.println("服务端启动完毕"); ServerSocket ss=new ServerSocket(8888); while (true) { Socket socket = ss.accept();//阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象 list.add(socket); System.out.println("有人上线了"+socket.getRemoteSocketAddress()); ex.execute(new MyServer(socket)); } } } public class MyServer implements Runnable{ private Socket socket; public MyServer(Socket socket){ this.socket=socket; } @Override public void run(){ try { OutputStream os = socket.getOutputStream(); PrintStream ps=new PrintStream(os); ps.println("HTTP/1.1 200 OK"); ps.println("Content-Type:text/html;charset=UTF-8"); ps.println(); // 必须换行 ps.println("<div style='color:red;font-size:120px;text-align:center'>黑马程序员666<div>"); ps.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); }
} }
|
Java高级
单元测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| /** * 字符串工具类 */ public class StringUtil { public static void printNumber(String name){ if(name == null){ System.out.println(0); return; // 停掉方法 } System.out.println("名字长度是:" + name.length()); }
/** * 求字符串的最大索引 */ public static int getMaxIndex(String data){ if(data == null) { return -1; } return data.length() - 1; }
} /* Junit单元测试框架 JUnit是使用Java语言实现的单元测试框架,它是第三方公司开源出来的,很多开发工具已经集成了Junit框架,比如 IDEA。 优点 编写的测试代码很灵活,可以指某个测试方法执行测试,也支持一键完成自动化测试。 不需要程序员去分析测试的结果,会自动生成测试报告出来。 提供了更强大的测试能力。 */ /* Junit单元测试-快速入门 某个系统,有多个业务方法,请使用Junit框架完成对这些方法的单元测试。 具体步骤 ① 将Junit框架的jar包导入到项目中(注意:IDEA集成了Junit框架,不需要我们自己手工导入了) ② 编写测试类、测试类方法(注意:测试方法必须是公共的,无参数,无返回值的非静态方法) ③ 必须在测试方法上使用@Test注解(标注该方法是一个测试方法) ④ 在测试方法中,编写程序调用被测试的方法即可。 ⑤ 选中测试方法,右键选择“JUnit运行” ,如果测试通过则是绿色;如果测试失败,则是红色 */
/*只有一个通信管道的话就用带class的 开始执行的方法:初始化资源。 执行完之后的方法:释放资源。 */ public class UtilTest { @BeforeClass public static void test11(){ System.out.println("test11BeforeClass执行了"); } @Before public void test1(){ System.out.println("test1Before执行了"); } @After public void test2(){ System.out.println("test2After执行了"); } @AfterClass public static void test22(){ System.out.println("test22AfterClass执行了"); } @Test public void TestPrintNumber(){ StringUtil.printNumber(null); } @Test public void TestGetMaxIndex(){ int index = StringUtil.getMaxIndex("hihi"); System.out.println(index); //断言机制 Assert.assertEquals("还是有误",3,index);//消息,预测,实际 } }
|
反射
注解
动态代理