Java网络编程中的常用类
Java为了跨平台,在网络应用通信时是不允许直接调用操作系统接 口的,而是由java.net包来提供网络功能。下面我们来介绍几个 java.net包中的常用的类。
InetAddress的使用
作用:封装计算机的IP地址和DNS(没有端口信息)
注:DNS是Domain Name System,域名系统。
特点:
这个类没有构造方法。如果要得到对象,只能通过静态方法:
getLocalHost()、getByName()、 getAllByName()、 getAddress()、getHostName()
获取本机信息
获取本机信息需要使用getLocalHost方法创建InetAddress对象。 getLocalHost()方法返回一个InetAddress对象,这个对象包含了本 机的IP地址,计算机名等信息。
public class InetTest { public static void main(String[] args)throws Exception { //实例化InetAddress对象 InetAddress inetAddress = InetAddress.getLocalHost(); //返回当前计算机的IP地址 System.out.println(inetAddress.getHostAddress()); //返回当前计算机名 System.out.println(inetAddress.getHostName()); } }
根据域名获取计算机的信息
根据域名获取计算机信息时需要使用getByName(“域名”)方法创建 InetAddress对象。
public class InetTest2 { public static void main(String[] args)throws Exception { InetAddress inetAddress = InetAddress.getByName("www.baidu.com"); System.out.println(inetAddress.getHostAddress()); System.out.println(inetAddress.getHostName()); } }
根据IP获取计算机的信息
根据IP地址获取计算机信息时需要使用getByName(“IP”)方法创建 InetAddress对象。
public class InetTest3 { public static void main(String[] args)throws Exception { InetAddress inetAddress = InetAddress.getByName("14.215.177.38"); System.out.println(inetAddress.getHostAddress()); System.out.println(inetAddress.getHostName()); } }
InetSocketAddress的使用
作用:包含IP和端口信息,常用于Socket通信。此类实现 IP 套接字 地址(IP 地址 + 端口号),不依赖任何协议。 InetSocketAddress相比较InetAddress多了一个端口号,端口的作 用:一台拥有IP地址的主机可以提供许多服务,比如Web服务、 FTP服务、SMTP服务等,这些服务完全可以通过1个IP地址来实 现。 那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址, 因为IP 地址与网络服务的关系是一对多的关系。实际上是通过“IP地 址+端口号”来区分不同的服务的。
public class InetSocketTest { public static void main(String[] args) { InetSocketAddress inetSocketAddress = new InetSocketAddress("www.baidu.com",80); System.out.println(inetSocketAddress.getAddress().getHostAddress()); System.out.println(inetSocketAddress.getHostName()); } }
URL的使用
IP地址标识了Internet上唯一的计算机,而URL则标识了这些计算机 上的资源。 URL 代表一个统一资源定位符,它是指向互联网“资源” 的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对 象的引用,例如对数据库或搜索引擎的查询。 为了方便程序员编程,JDK中提供了URL类,该类的全名是 java.net.URL,有了这样一个类,就可以使用它的各种方法来对 URL对象进行分割、合并等处理。
public class UrlTest { public static void main(String[] args)throws Exception { URL url = new URL("https://www.itbaizhan.com/search.html?kw=java"); System.out.println("获取与此URL相关联协议的默认端口:"+url.getDefaultPort()); System.out.println("访问资源:"+url.getFile()); System.out.println("主机名"+url.getHost()); System.out.println("访问资源路径:"+url.getPath()); System.out.println("协议:"+url.getProtocol()); System.out.println("参数部分:"+url.getQuery()); } }
通过URL实现最简单的网络爬虫
public class UrlTest2{ public static void main(String[] args)throws Exception { URL url = new URL("https://www.itbaizhan.com/"); try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { StringBuilder sb = new StringBuilder(); String temp; /* * 这样就可以将网络内容下载到本地机器。 * 然后进行数据分析,建立索引。这也是搜索引擎的第一步。 */ while ((temp = br.readLine()) != null) { sb.append(temp); } System.out.println(sb); } catch (Exception e) { e.printStackTrace(); } } }
TCP通信的实现和项目案例
TCP通信实现原理
前边我们提到TCP协议是面向的连接的,在通信时客户端与服务器 端必须建立连接。在网络通讯中,第一次主动发起通讯的程序被称 作客户端(Client)程序,简称客户端,而在第一次通讯中等待连接的 程序被称作服务器端(Server)程序,简称服务器。一旦通讯建立,则 客户端和服务器端完全一样,没有本质的区别。
“请求-响应”模式:
Socket类:发送TCP消息。
ServerSocket类:创建服务器。
套接字Socket是一种进程间的数据交换机制。这些进程既可以在同 一机器上,也可以在通过网络连接的不同机器上。换句话说,套接 字起到通信端点的作用。单个套接字是一个端点,而一对套接字则 构成一个双向通信信道,使非关联进程可以在本地或通过网络进行 数据交换。一旦建立套接字连接,数据即可在相同或不同的系统中 双向或单向发送,直到其中一个端点关闭连接。套接字与主机地址 和端口地址相关联。主机地址就是客户端或服务器程序所在的主机 的IP地址。端口地址是指客户端或服务器程序使用的主机的通信端 口。 在客户端和服务器中,分别创建独立的Socket,并通过Socket的属 性,将两个Socket进行连接,这样,客户端和服务器通过套接字所 建立的连接使用输入输出流进行通信。 TCP/IP套接字是最可靠的双向流协议,使用TCP/IP可以发送任意数 量的数据。 实际上,套接字只是计算机上已编号的端口。如果发送方和接收方 计算机确定好端口,他们就可以通信了。 客户端与服务器端的通信关系图:
TCP/IP通信连接的简单过程:
位于A计算机上的TCP/IP软件向B计算机发送包含端口号的消息,B 计算机的TCP/IP软件接收该消息,并进行检查,查看是否有它知道 的程序正在该端口上接收消息。如果有,他就将该消息交给这个程 序。 要使程序有效地运行,就必须有一个客户端和一个服务器。
通过Socket的编程顺序:
1 创建服务器ServerSocket,在创建时,定义ServerSocket的监听端口(在这个端口接收客户端发来 的消息)
2 ServerSocket调用accept()方法,使之处于阻塞状态。
3 创建客户端Socket,并设置服务器的IP及端口。
4 客户端发出连接请求,建立连接。
5 分别取得服务器和客户端Socket的InputStream和OutputStream。
6 利用Socket和ServerSocket进行数据传输。
7 关闭流及Socket。
TCP通信入门案例
创建服务端
public class BasicSocketServer { public static void main(String[] args) { System.out.println("服务器启动等待监听。。。。"); //创建ServerSocket try(ServerSocket ss =new ServerSocket(8888); //监听8888端口,此时线程会处于阻塞状态。 Socket socket = ss.accept(); //连接成功后会得到与客户端对应的 Socket对象,并解除线程阻塞。 //通过客户端对应的Socket对象中的输入流对象,获取客户端发送过来的消息。 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())) ){ System.out.println(br.readLine()); }catch(Exception e){ e.printStackTrace(); System.out.println("服务器启动失败。。。。"); } } }
创建客户端
public class BasicSocketClient { public static void main(String[] args) { //创建Socket对象 try(Socket socket =new Socket("127.0.01",8888); //创建向服务端发送消息的输出流对象。 PrintWriter pw = new PrintWriter(socket.getOutputStream())){ pw.println("服务端,您好!"); pw.flush(); }catch(Exception e){ e.printStackTrace(); } } }
TCP单向通信
单向通信是指通信双方中,一方固定为发送端,一方则固定为接收 端。
创建服务端
public class OneWaySocketServer { public static void main(String[] args) { System.out.println("服务端启动,开始监听。。。。。"); try(ServerSocket serverSocket = new ServerSocket(8888); //监听8888端口,获与取客户端对应的 Socket对象 Socket socket = serverSocket.accept(); //通过与客户端对应的Socket对象获取输入流对象 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //通过与客户端对应的Socket对象获取输出流对象 PrintWriter pw = new PrintWriter(socket.getOutputStream())){ System.out.println("连接成功!"); while(true){ //读取客户端发送的消息 String str = br.readLine(); System.out.println("客户端说:"+str); if("exit".equals(str)){ break; } pw.println(str); pw.flush(); } }catch(Exception e){ e.printStackTrace(); System.out.println("服务端启动失败。。。。。"); } } }
创建客户端
public class OneWaySocketClient { public static void main(String[] args) { //获取与服务端对应的Socket对象 try(Socket socket = new Socket("127.0.0.1",8888); //创建键盘输入对象 Scanner scanner = new Scanner(System.in); //通过与服务端对应的Socket对象获取输出流对象 PrintWriter pw = new PrintWriter(socket.getOutputStream()); //通过与服务端对应的Socket对象获取输入流对象 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { while(true){ //通过键盘输入获取需要向服务端发送的消息 String str = scanner.nextLine(); //将消息发送到服务端 pw.println(str); pw.flush(); if("exit".equals(str)){ break; } //读取服务端返回的消息 String serverInput = br.readLine(); System.out.println("服务端返回的:"+serverInput); } }catch(Exception e){ e.printStackTrace(); } } }
TCP双向通信
双向通信是指通信双方中,任何一方都可为发送端,任何一方都可 为接收端。
创建服务端
public class TwoWaySocketServer { public static void main(String[] args) { System.out.println("服务端启动!监听端口8888。。。。"); try(ServerSocket serverSocket = new ServerSocket(8888); Socket socket = serverSocket.accept(); //创建键盘输入对象 Scanner scanner = new Scanner(System.in); //通过与客户端对应的Socket对象获取输入流对象 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //通过与客户单对应的Socket对象获取输出流对象 PrintWriter pw = new PrintWriter(socket.getOutputStream());){ while(true){ //读取客户端发送的消息 String str = br.readLine(); System.out.println("客户端说:"+str); String keyInput = scanner.nextLine(); //发送到客户端 pw.println(keyInput); pw.flush(); } }catch(Exception e){ e.printStackTrace(); } } }
创建客户端
public class TwoWaySocketClient { public static void main(String[] args) { try(Socket socket = new Socket("127.0.0.1", 8888); //创建键盘输入对象 Scanner scanner = new Scanner(System.in); //通过与服务端对应的Socket对象获取输入流对象 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //通过与服务端对应的Socket对象获取输出流对象 PrintWriter pw = new PrintWriter(socket.getOutputStream());){ while (true) { String keyInput = scanner.nextLine(); pw.println(keyInput); pw.flush(); String input = br.readLine(); System.out.println("服务端说:" + input); } } catch (Exception e) { e.printStackTrace(); } } }
创建点对点的聊天应用
创建服务端
/** * 发送消息线程 */ class Send extends Thread{ private Socket socket; public Send(Socket socket){ this.socket = socket; } @Override public void run() { this.sendMsg(); } /** * 发送消息 */ private void sendMsg(){ //创建Scanner对象 try(Scanner scanner = new Scanner(System.in); //创建向对方输出消息的流对象 PrintWriter pw = new PrintWriter(this.socket.getOutputStream());) { while(true){ String msg = scanner.nextLine(); pw.println(msg); pw.flush(); } }catch(Exception e){ e.printStackTrace(); } } } /** * 接收消息的线程 */ class Receive extends Thread{ private Socket socket; public Receive(Socket socket){ this.socket = socket; } @Override public void run() { this.receiveMsg(); } /** * 用于接收对方消息的方法 */ private void receiveMsg(){ //创建用于接收对方发送消息的流对象 try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));){ while(true){ String msg = br.readLine(); System.out.println("他说:"+msg); } }catch(Exception e){ e.printStackTrace(); } } } public class ChatSocketServer { public static void main(String[] args) { try(ServerSocket serverSocket = new ServerSocket(8888);){ System.out.println("服务端启动,等待连接。。。。。"); Socket socket = serverSocket.accept(); System.out.println("连接成功!"); new Send(socket).start(); new Receive(socket).start(); }catch(Exception e){ e.printStackTrace(); } } }
创建客户端
/** * 用于发送消息的线程类 */ class ClientSend extends Thread{ private Socket socket; public ClientSend(Socket socket){ this.socket = socket; } @Override public void run() { this.sendMsg(); } /** * 发送消息 */ private void sendMsg(){ //创建Scanner对象 try(Scanner scanner = new Scanner(System.in); //创建向对方输出消息的流对象 PrintWriter pw = new PrintWriter(this.socket.getOutputStream());) { while(true){ String msg = scanner.nextLine(); pw.println(msg); pw.flush(); } }catch(Exception e){ e.printStackTrace(); } } } /** * 用于接收消息的线程类 */ class ClientReceive extends Thread{ private Socket socket; public ClientReceive(Socket socket){ this.socket = socket; } @Override public void run() { this.receiveMsg(); } /** * 用于接收对方消息的方法 */ private void receiveMsg(){ //创建用于接收对方发送消息的流对象 try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));){ while(true){ String msg = br.readLine(); System.out.println("他说:"+msg); } }catch(Exception e){ e.printStackTrace(); } } } public class ChatSocketClient { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1", 8888); System.out.println("连接成功!"); new ClientSend(socket).start(); new ClientReceive(socket).start(); }catch(Exception e){ e.printStackTrace(); } } }
优化点对点聊天应用
/** * 发送消息线程 */ class Send extends Thread{ private Socket socket; private Scanner scanner; public Send(Socket socket,Scanner scanner){ this.socket = socket; this.scanner = scanner; } @Override public void run() { this.sendMsg(); } /** * 发送消息 */ private void sendMsg(){ //创建向对方输出消息的流对象 try(PrintWriter pw = new PrintWriter(this.socket.getOutputStream())) { while(true){ String msg = scanner.nextLine(); pw.println(msg); pw.flush(); } }catch(Exception e){ e.printStackTrace(); } } } /** * 接收消息的线程 */ class Receive extends Thread{ private Socket socket; public Receive(Socket socket){ this.socket = socket; } @Override public void run() { this.receiveMsg(); } /** * 用于接收对方消息的方法 */ private void receiveMsg(){ //创建用于接收对方发送消息的流对象 try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()))){ while(true){ String msg = br.readLine(); System.out.println("他说:"+msg); } }catch(Exception e){ e.printStackTrace(); } } } public class GoodTCP { public static void main(String[] args) { Scanner scanner = null; ServerSocket serverSocket = null; Socket socket = null; try{ scanner = new Scanner(System.in); System.out.println("请输入:server,<port> 或者:<ip>,<port>"); String str = scanner.nextLine(); String[] arr = str.split(","); if("server".equals(arr[0])){ //启动服务端 System.out.println("TCP Server Listen at "+arr[1]+" ....."); serverSocket = new ServerSocket(Integer.parseInt(arr[1])); socket = serverSocket.accept(); System.out.println("连接成功!"); }else{ //启动客户端 socket = new Socket(arr[0],Integer.parseInt(arr[1])); System.out.println("连接成功!"); } //启动发送消息的线程 new Send(socket,scanner).start(); //启动接收消息的线程 }catch(Exception e){ e.printStackTrace(); }finally{ if(serverSocket != null){ try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
一对多应用
一对多应用设计
各socket对间独立问答,互相间不需要传递信息。
一对多应答型服务器
/** * 定义消息处理线程类 */ class Msg extends Thread{ private Socket socket; public Msg(Socket socket){ this.socket = socket; } @Override public void run() { this.msg(); } /** * 将从客户端读取到的消息写回给客户端 */ private void msg(){ try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); PrintWriter pw = new PrintWriter(this.socket.getOutputStream())){ while(true){ pw.println(br.readLine()+" [ok]"); pw.flush(); } }catch(Exception e){ e.printStackTrace(); System.out.println(this.socket.getInetAddress()+" 断线了!"); } } } public class EchoServer { public static void main(String[] args) { try(ServerSocket serverSocket = new ServerSocket(8888)){ //等待多客户端连接 while(true){ Socket socket = serverSocket.accept(); new Msg(socket).start(); } }catch(Exception e){ e.printStackTrace(); } } }
一对多聊天服务器
服务器设计
1、服务器的连接设计
2、服务器的线程设计
创建一对多聊天服务应用
/** * 接收客户端消息的线程类 */ class ChatReceive extends Thread{ private Socket socket; public ChatReceive(Socket socket){ this.socket =socket; } @Override public void run() { this.receiveMsg(); } /** * 实现接收客户端发送的消息 */ private void receiveMsg(){ try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()))){ while(true){ String msg = br.readLine(); synchronized ("abc"){ //把读取到的数据写入公共数据区 ChatRoomServer.buf=" ["+this.socket.getInetAddress()+"] "+msg; //唤醒发送消息的线程对象。 "abc".notifyAll(); } } }catch(Exception e){ e.printStackTrace(); } } } /** * 向客户端发送消息的线程类 */ class ChatSend extends Thread{ private Socket socket; public ChatSend(Socket socket){ this.socket = socket; } @Override public void run() { this.sendMsg(); } /** * 将公共数据区的消息发送给客户端 */ private void sendMsg(){ try(PrintWriter pw = new PrintWriter(this.socket.getOutputStream())){ while(true){ synchronized ("abc"){ //让发送消息的线程处于等待状态 "abc".wait(); //将公共数据区中的消息发送给客户端 pw.println(ChatRoomServer.buf); pw.flush(); } } }catch(Exception e){ e.printStackTrace(); } } } public class ChatRoomServer { //定义公共数据区 public static String buf; public static void main(String[] args) { System.out.println("Chat Server Version 1.0"); System.out.println("Listen at 8888....."); try(ServerSocket serverSocket = new ServerSocket(8888)){ while(true){ Socket socket = serverSocket.accept(); System.out.println("连接到:"+socket.getInetAddress()); new ChatReceive(socket).start(); new ChatSend(socket).start(); } }catch(Exception e){ e.printStackTrace(); } } }