Java网络编程 一、基础知识 IP:
示例:
1 2 3 4 5 6 7 8 9 10 InetAddress localHost = InetAddress.getLocalHost();System.out.println(localHost.getHostName()); System.out.println(localHost.getHostAddress()); InetAddress ip2 = InetAddress.getByName("www.zhihu.com" );System.out.println(ip2.getHostName()); System.out.println(ip2.getHostAddress()); System.out.println(ip2.isReachable(6000 ));
执行结果:
端口: 用于标记计算机设备上运行的应用程序,被规定为一个16位的二进制,范围是0~65535。
协议 1.UDP(用户数据报协议) 特点:无连接、不可靠通信
解释:无连接指的是数据传输之前不事先建立连接。不可靠通信指的是发送端每次把发送的数据(不超过64KB)、接收端IP等信息封装成一个数据包,把这个数据包发出去后就不管了。管它接收端有没有接收到。
java涉及api:
案例
服务端:
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 public class UDPService { public static void main (String[] args) throws Exception { DatagramSocket socket = new DatagramSocket (6666 ); System.out.println("服务端启动" ); byte [] bytes = new byte [1024 * 64 ]; DatagramPacket packet = new DatagramPacket (bytes, bytes.length); while (true ){ socket.receive(packet); String clientAddress = packet.getAddress().getHostAddress(); int port = packet.getPort(); System.out.println("收到客服端:" +clientAddress+":" +port+"的消息" ); int length = packet.getLength(); System.out.println("数据:" + new String (packet.getData(), 0 , length)); System.out.println("--------------------" ); } } }
客户端:
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 public class UDPClient { public static void main (String[] args) throws Exception{ DatagramSocket socket = new DatagramSocket (); Scanner sc = new Scanner (System.in); while (true ){ System.out.println("请输入要发送的信息:" ); String msg = sc.nextLine(); byte [] bytes = msg.getBytes(); DatagramPacket packet = new DatagramPacket (bytes, bytes.length, InetAddress.getLocalHost(), 6666 ); socket.send(packet); System.out.println("------------------------" ); } } }
2.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 public class TCPService { public static void main (String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket (6666 ); Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream(); DataInputStream dis = new DataInputStream (is); while (true ){ try { String s = dis.readUTF(); System.out.println(s); System.out.println("客户端地址" +socket.getRemoteSocketAddress()); }catch (Exception e){ System.out.println(socket.getRemoteSocketAddress()+"离线了" ); socket.close(); dis.close(); break ; } } } }
客户端
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 public class TCPClient { public static void main(String[] args) throws IOException { //1.创建Socket对象,它默认是由TCP传输协议,host和port是收数据的服务端 Socket socket = new Socket("localhost", 6666); //2.从Socket通信管道中得到一个字节输出流 OutputStream os =socket.getOutputStream(); //3.把低级的字节输出流包装成数据输出流 DataOutputStream dos = new DataOutputStream(os); Scanner sc = new Scanner(System.in); //4.写数据 while(true){ String msg = sc.nextLine(); if(msg.equals("exit")){ dos.close(); socket.close(); break; } dos.writeUTF(msg); //将缓冲区的数据立即写入到目标设备,确保消息能立即发送给服务端 dos.flush(); } } }
上面的案例因为只有一个线程来处理请求,所以无法应多个客户端同时发起连接的情况。所以我们可以使用多线程来处理,服务端主线负责异步线程的创建。
支持一服务端与多客户端同时通信 测试时,记得给在idea中设置客户端可以多开
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class TCPService { public static void main (String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket (6666 ); while (true ){ Socket socket = serverSocket.accept(); System.out.println(socket.getRemoteSocketAddress() + "上线了" ); new TCPReadThread (socket).start(); } } }
自定义异步线程
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 public class TCPReadThread extends Thread { Socket socket; public TCPReadThread (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(socket.getRemoteSocketAddress() + "消息:" + msg); }catch (Exception e){ System.out.println(socket.getRemoteSocketAddress() + "下线了" ); dis.close(); socket.close(); break ; } } }catch (Exception e){ System.out.println("获取输入流异常" ); } } }
客户端
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 public class TCPClient { public static void main (String[] args) throws IOException { Socket socket = new Socket ("localhost" , 6666 ); OutputStream os = socket.getOutputStream(); DataOutputStream dos = new DataOutputStream (os); Scanner sc = new Scanner (System.in); while (true ){ String msg = sc.nextLine(); if (msg.equals("exit" )){ dos.close(); socket.close(); System.out.println("欢迎您下次光临" ); break ; } dos.writeUTF(msg); dos.flush(); } } }
即时通讯—群聊 客户端
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 public class TCPClient { public static void main (String[] args) throws IOException { Socket socket = new Socket ("localhost" , 6666 ); new ClientReadThread (socket).start(); OutputStream os = socket.getOutputStream(); DataOutputStream dos = new DataOutputStream (os); Scanner sc = new Scanner (System.in); while (true ){ String msg = sc.nextLine(); if (msg.equals("exit" )){ dos.close(); socket.close(); System.out.println("欢迎您下次光临" ); break ; } dos.writeUTF(msg); dos.flush(); } } }
客户端消息获取线程
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 public class ClientReadThread extends Thread { Socket socket; public ClientReadThread (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 (IOException e) { dis.close(); socket.close(); break ; } } } catch (IOException e) { throw new RuntimeException (e); } } }
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class TCPService { public static ArrayList<Socket> onLineSocket = new ArrayList <>(); public static void main (String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket (6666 ); while (true ){ Socket socket = serverSocket.accept(); onLineSocket.add(socket); new ServiceReadThread (socket).start(); } } }
服务端消息获取线程
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 public class ServiceReadThread extends Thread { Socket socket; public ServiceReadThread (Socket socket) { this .socket = socket; } @Override public void run () { try { System.out.println(socket.getRemoteSocketAddress() + "上线了" ); sendToAllClient(socket.getRemoteSocketAddress() + "上线了" ); InputStream is = socket.getInputStream(); DataInputStream dis = new DataInputStream (is); while (true ){ String msg; try { msg = dis.readUTF(); System.out.println(socket.getRemoteSocketAddress() + "消息:" + msg); }catch (Exception e){ System.out.println(socket.getRemoteSocketAddress() + "下线了" ); sendToAllClient(socket.getRemoteSocketAddress() + "下线了" ); dis.close(); socket.close(); break ; } sendToAllClient(socket.getRemoteSocketAddress() + ": " +msg); } }catch (Exception e){ System.out.println("获取输入流异常" ); } TCPService.onLineSocket.remove(socket); } private void sendToAllClient (String msg) throws IOException { for (Socket sc : TCPService.onLineSocket) { if (!sc.getRemoteSocketAddress().toString().equals(socket.getRemoteSocketAddress().toString())){ OutputStream os = sc.getOutputStream(); DataOutputStream dos = new DataOutputStream (os); dos.writeUTF(msg); dos.flush(); } } } }
BS架构请求 浏览器也可以对本地开放的端口进行访问,这时就不要客户端了,但返回给浏览器的数据必须要严格按照浏览器请求的协议接收的数据格式进行返回。
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class TCPService { public static ThreadPoolExecutor pool = new ThreadPoolExecutor (3 , 5 , 1000 , TimeUnit.SECONDS, new ArrayBlockingQueue <>(5 ), Executors.defaultThreadFactory(), new ThreadPoolExecutor .AbortPolicy()); public static void main (String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket (8080 ); while (true ){ Socket socket = serverSocket.accept(); pool.execute(new ServerReadThread (socket)); } } }
服务端获取数据线程
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 public class ServerReadThread extends Thread { Socket socket; public ServerReadThread (Socket socket) { this .socket = socket; } @Override public void run () { try { System.out.println(socket.getRemoteSocketAddress() + "发起请求" ); 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=16px;text-align:center'> ldy你好 </div>" ); ps.close(); socket.close(); System.out.println(socket.getRemoteSocketAddress() + "请求已关闭" ); } catch (IOException e) { throw new RuntimeException (e); } } }
二、面试知识 TCP三次握手 三次握手:为了确保双方收发消息都没问题 。
假设客户端发出连接请求
第一次握手:客户端发出连接请求,服务端收到请求后,知道客户端发消息没问题。
第二次握手:服务端返回一个响应。客户端收到响应后,知道服务端收消息和发消息都没问题。
第三次握手:客户端再次发送确认信息给服务端。服务端收到消息后,知道客户端接收消息没问题。
这样,经过三次,客户端和服务端都知道对方可以正常收发消息,就可以开始进行通信了。
TCP四次挥手 四次挥手:确保双方的数据收发都已经完毕,保证断开的可靠性
第一次挥手:客户端发起断开连接请求
第二次挥手:服务器端可能有客户端的请求还在处理,所以先返回一个稍等的响应
第三次挥手:服务器端把数据处理完毕之后,确定自己没有数据要收和发了之后,返回确认断开的响应
第四次挥手:客户端在处理完毕服务器端的数据后,确定自己没有数据要收和发了,处理完成后,发出正式确认断开连接的请求。