Java网络编程
OSI模型分类:
1)应用层:与其他计算机进行通讯的一个应用,它是对应应用程序的通信服务的。
2)表示层:这一层的主要功能是定义数据格式及加密。
3)会话层:他定义了如何开始、控制和结束一个会话,包括对多个双向小时的控制和管理,以便在只完成连续消息的一部分时可以通知应用,从而使表示层看到的数据是连续的,在某些情况下,如果表示层收到了所有的数据,则用数据代表表示层。
4)传输层:这层的功能包括是否选择差错恢复协议还是无差错恢复协议,及在同一主机上对不同应用的数据流的输入进行复用,还包括对收到的顺序不对的数据包的重新排序功能。
5)网络层:这层对端到端的包传输进行定义,他定义了能够标识所有结点的逻辑地址,还定义了路由实现的方式和学习的方式。
6)数据链路层:他定义了在单个链路上如何传输数据。这些协议与被讨论的各种介质有关。示例:ATM,FDDI等。
7)物理层:OSI的物理层规范是有关传输介质的特性标准,这些规范通常也参考了其他组织制定的标准。连接头、针、针的使用、电流、电流、编码及光调制等都属于各种物理层规范中的内容。
TCP/IP分层模型:
第一层 网络接口层:网络接口层包括用于协作IP数据在已有网络介质上传输的协议。
第二层 网络层:网间层对应于OSI七层参考模型的网络层。本层包含IP协议、RIP协议,负责数据的包装、寻址和路由。同时还包含网间控制报文协议用来提供网络诊断信息。
第三层 传输层:传输层对应于OSI七层参考模型的传输层,它提供两种端到端的通信服务。其中TCP(传输控制协议)协议提供可靠的数据流运输服务,UDP(用户报协议)协议提供不可靠的用户数据报服务。
第四层 应用层:应用层对应于OSI七层参考模型的应用层和表达层。
注意:端口的号码必须位于0-65535之间,每个端口唯一的对应一个网络程序,一个网络程序可以使用多个端口。
域名是使用简洁的有意义的字符代替IP地址的方式,一个IP可以对应多个域名,但一个域名只能对应一个IP地址。将域名解析为IP地址,然后将解析后的IP地址反馈给浏览器,再进行实际的数据传输。
客户端和服务器:
客户端:是通讯 的发起者
服务器:程序被动等待客户端发起通信, 并对其做出响应
Java对网络编程的支持:
InetAddress类代表IP地址,他没有构造器。
有如下方法:
getLocalHost()方法:可以得到描述本机IP的InetAddress对象。
import java.net.InetAddress; /** * InetAddres测试类 * * */ public class MyInetAddress { public static void main(String[] args) throws Exception { // 得到描述本机IP的InetAddress对象 InetAddress localAddress = InetAddress.getLocalHost(); // 打印 System.out.println(localAddress); } }
getByName()方法:可以通过指定域名从DNS中得到相应的IP地址。getByName一个String类型参数,可以通过这个参数指定远程主机的域名
import java.net.InetAddress; /** * InetAddres测试类 * * */ public class MyInetAddress { public static void main(String[] args) throws Exception { String host = "www.baidu.com"; InetAddress address = InetAddress.getByName(host); System.out.println(address); } }
getAllByName()方法:可以从DNS上得到域名对应的所有的IP。这个方法返回一个InetAddress类型的数组。
import java.net.InetAddress; /** * InetAddres测试类 * * */ public class MyInetAddress { public static void main(String[] args) throws Exception { String host = "www.baidu.com"; InetAddress addresses[] = InetAddress.getAllByName(host); for (InetAddress address : addresses) System.out.println(address); } }
getByAddress()方法:必须通过IP地址来创建InetAddress对象,而且IP地址必须是byte数组形式。getByAddress方法有两个重载形式
import java.net.InetAddress; /** * InetAddres测试类 * * */ public class MyInetAddress { public static void main(String[] args) throws Exception { byte ip[] = new byte[] { (byte) 180, (byte) 97, 33, 108 }; InetAddress address1 = InetAddress.getByAddress(ip); InetAddress address2 = InetAddress.getByAddress("百度官方网站", ip); System.out.println(address1); System.out.println(address2); } }
TCP套接字编程(ServerSocket类和Socket类):
服务器端:
1 import java.awt.BorderLayout; 2 import java.awt.event.ActionEvent; 3 import java.awt.event.ActionListener; 4 import java.io.BufferedOutputStream; 5 import java.io.BufferedReader; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.net.ServerSocket; 9 import java.net.Socket; 10 11 import javax.swing.JButton; 12 import javax.swing.JFrame; 13 import javax.swing.JPanel; 14 import javax.swing.JScrollPane; 15 import javax.swing.JTextArea; 16 import javax.swing.JTextField; 17 18 /** 19 * 服务器端窗体类 20 * 21 * @author 小明 22 * 23 */ 24 public class Server extends JFrame { 25 26 private static final long serialVersionUID = 6206072788856936392L; 27 28 private JTextArea info; // 显示消息的文本区 29 private Socket socket; // 客户端Socket对象 30 31 public Server(String title) { 32 super(title); // 标题 33 34 setSize(200, 300); // 窗体大小 35 setLocation(200, 300); // 窗体坐标 36 setDefaultCloseOperation(EXIT_ON_CLOSE); // 默认关闭操作 37 38 info = new JTextArea(); // 创建文本区对象 39 info.setEditable(false); // 文本区不可编辑 40 info.setFocusable(false); // 文本区不可获得焦点 41 JScrollPane jsp = new JScrollPane(info); // 创建滚动面板 42 this.add(jsp, BorderLayout.CENTER); // 将滚动面板添加到窗体中 43 44 JPanel pnl = new JPanel(new BorderLayout()); // 创建面板 45 final JTextField msg = new JTextField(); // 创建输入文本框 46 JButton send = new JButton("发送"); // 创建发送按钮 47 pnl.add(msg, BorderLayout.CENTER); // 将输入文本框添加到面板 48 pnl.add(send, BorderLayout.EAST); // 将发送按钮添加到面板 49 this.add(pnl, BorderLayout.SOUTH); // 将面板添加到窗体中 50 51 setVisible(true); // 设置窗体可见 52 53 startServer(); // 启动服务器 54 55 // 注册“发送”按钮的事件监听器 56 send.addActionListener(new ActionListener() { 57 @Override 58 public void actionPerformed(ActionEvent e) { 59 try { 60 // 创建缓冲区字节输出流对象 61 BufferedOutputStream out = new BufferedOutputStream(socket 62 .getOutputStream()); 63 // 写入输出流 64 out.write((msg.getText() + "\n").getBytes()); 65 // 刷出到客户端 66 out.flush(); 67 } catch (IOException ex) { 68 throw new RuntimeException(ex); 69 } 70 } 71 }); 72 } 73 74 /** 75 * 启动服务器 76 */ 77 private void startServer() { 78 try { 79 // 创建ServerSocket对象,指定监听的端口 80 ServerSocket server = new ServerSocket(30000); 81 // 等待客户端连接 82 socket = server.accept(); 83 // 读客户端发送消息 84 read(); 85 } catch (IOException e) { 86 throw new RuntimeException(e); 87 } 88 } 89 90 /** 91 * 读客户端消息 92 */ 93 private void read() { 94 try { 95 // 创建缓冲区字符输入流 96 final BufferedReader reader = new BufferedReader( 97 new InputStreamReader(socket.getInputStream())); 98 // 开启新线程用于专门读数据 99 new Thread(new Runnable() { 100 @Override 101 public void run() { 102 char[] ch = new char[1024]; 103 // 读输入流中的数据 104 while (true) { 105 int len; 106 try { 107 len = reader.read(ch); 108 info.append(new String(ch, 0, len)); // 追加到文本区显示 109 } catch (IOException e) { 110 throw new RuntimeException(e); 111 } 112 } 113 } 114 }).start(); 115 } catch (IOException e) { 116 throw new RuntimeException(e); 117 } 118 } 119 120 public static void main(String[] args) { 121 new Server("服务器"); 122 } 123 }
客户端:
1 import java.awt.BorderLayout; 2 import java.awt.event.ActionEvent; 3 import java.awt.event.ActionListener; 4 import java.io.BufferedOutputStream; 5 import java.io.BufferedReader; 6 import java.io.IOException; 7 import java.io.InputStreamReader; 8 import java.net.Socket; 9 10 import javax.swing.JButton; 11 import javax.swing.JFrame; 12 import javax.swing.JPanel; 13 import javax.swing.JScrollPane; 14 import javax.swing.JTextArea; 15 import javax.swing.JTextField; 16 17 /** 18 * 客户端窗体类 19 * 20 * @author 小明 21 * 22 */ 23 public class Client extends JFrame { 24 25 private static final long serialVersionUID = 1173239888462689029L; 26 27 private JTextArea info; // 显示消息的文本区 28 private Socket socket; // 客户端Socket对象 29 30 public Client(String title) { 31 super(title); // 标题 32 33 setSize(200, 300); // 窗体大小 34 setLocation(700, 300); // 窗体坐标 35 setDefaultCloseOperation(EXIT_ON_CLOSE); // 默认关闭操作 36 37 info = new JTextArea(); // 创建文本区对象 38 info.setEditable(false); // 文本区不可编辑 39 info.setFocusable(false); // 文本区不可获得焦点 40 JScrollPane jsp = new JScrollPane(info); // 创建滚动面板 41 this.add(jsp, BorderLayout.CENTER); // 将滚动面板添加到窗体中 42 43 JPanel pnl = new JPanel(new BorderLayout()); // 创建面板 44 final JTextField msg = new JTextField(); // 创建输入文本框 45 JButton send = new JButton("发送"); // 创建发送按钮 46 pnl.add(msg, BorderLayout.CENTER); // 将输入文本框添加到面板 47 pnl.add(send, BorderLayout.EAST); // 将发送按钮添加到面板 48 this.add(pnl, BorderLayout.SOUTH); // 将面板添加到窗体中 49 50 setVisible(true); // 设置窗体可见 51 52 startClient(); // 启动客户端 53 54 // 注册“发送”按钮的事件监听器 55 send.addActionListener(new ActionListener() { 56 @Override 57 public void actionPerformed(ActionEvent e) { 58 try { 59 // 创建缓冲区字节输出流对象 60 BufferedOutputStream out = new BufferedOutputStream(socket 61 .getOutputStream()); 62 // 写入输出流 63 out.write((msg.getText() + "\n").getBytes()); 64 // 刷出到服务器端 65 out.flush(); 66 } catch (IOException ex) { 67 throw new RuntimeException(ex); 68 } 69 } 70 }); 71 } 72 73 /** 74 * 启动客户端 75 */ 76 private void startClient() { 77 try { 78 // 创建Socket对象,指定连接的服务器与连接的端口 79 socket = new Socket("127.0.0.1", 30000); 80 // 读服务端发送消息 81 read(); 82 } catch (IOException e) { 83 throw new RuntimeException(e); 84 } 85 } 86 87 /** 88 * 读服务器端消息 89 */ 90 private void read() { 91 try { 92 // 创建缓冲区字符输入流 93 final BufferedReader reader = new BufferedReader( 94 new InputStreamReader(socket.getInputStream())); 95 // 开启新线程用于专门读数据 96 new Thread(new Runnable() { 97 @Override 98 public void run() { 99 char[] ch = new char[1024]; 100 // 读输入流中的数据 101 while (true) { 102 int len; 103 try { 104 len = reader.read(ch); 105 info.append(new String(ch, 0, len)); // 追加到文本区显示 106 } catch (IOException e) { 107 throw new RuntimeException(e); 108 } 109 } 110 } 111 }).start(); 112 } catch (IOException e) { 113 throw new RuntimeException(e); 114 } 115 } 116 117 public static void main(String[] args) { 118 new Client("客户端"); 119 } 120 }
UDP套接字编程:
UDP(用户数据报协议)是用于将二进制数据从一台计算机发送到另一台计算机的非连接协议。在Java中数据报包的发送者与接收者都使用java.net.DatagramSocket类发送和接收包。
DatagramSocket(发送和接收包)的构造方法如下:
- public DatagramSocket() throws SocketException
- public DatagramSocket(SocketAddress bindaddr) throws SocketException
- public DatagramSocket(int port) throws SocketException
- public DatagramSocket(int port, InetAddress laddr) throws SocketException
常用方法:
- public void send(DatagramPacket p) throws IOException
- public void receive(DatagramPacket p) throws IOException
DatagramPacket代表一个数据报包,与DatagramSocket类似,包的发送者与接收者都要使用到它。DatagramPacket有六个构造方法,其中两个由接收者使用,四个由发送者使用。
接收数据报包:
- public DatagramPacket(byte[] buf, int length):用来接收长度为 length 的数据包,buf为保存传入数据报的缓冲区。
- public DatagramPacket(byte[] buf, int offset, int length):用来接收长度为 length 的包,在缓冲区buf中指定了偏移量offset。
发送数据报包:
- public DatagramPacket(byte[] buf, int length, InetAddress address, int port)
- public DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
- public DatagramPacket(byte[] buf, int length, SocketAddress address) throws SocketException
- public DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) throws SocketException
接收数据报包
接收数据报包的执行步骤如下:
- 创建一个足够大的字节数组,用于存储要接收的数据报包的数据。
- 使用该字节数组实例化一个DatagramPacket对象。
- DatagramSocket被实例化,它被指定到套接字要绑定的本地主机上的一个端口。
- 调用DatagramSocket类的receive()方法,将DatagramPacket对象传递到方法中,这将导致执行线程阻塞,直到接收一个数据报包或超时。
发送数据报包
发送数据报包的执行步骤如下:
- 创建一个足够大的字节数组,用于存储要发送的数据报包的数据。
- 创建DatagramPacket对象,用于存储该字节数组,以及服务器的名称和接收者的端口号。
- DatagramSocket被实例化,它被指定到套接字要绑定的本地主机上的一个端口。
- 调用DatagramSocket类的send()方法,传递DatagramPacket对象参数,发送数据报包。