网络通信
- java最初是作为一种控制家电的语言被开发出来的。当初设计该语言时,主旨之一是连接机器,而现在仍是如此。
- java.net包让java能够通过网络进行通信。结合使用输入输出流,通过网络读写文件几乎和读写本地文件一样容易。
- 要同网络上的系统进行通信,可以采用三种简单的方式:
(1)在小程序中使用URL装载Web页和其他资源。
(2)使用套接字类Socket和ServerSocket,它们建立到主机的标准套接字连接,并通过这种连接执行读写。
(3)调用getInputStream(),该方法建立到URL的连接,并通过该连接来获取数据。
网络基本知识
- 两台计算机间进行通讯需要以下三个条件:IP地址、协议、端口号
- TCP/IP协议是目前世界上应用最为广泛的协议,是以TCP和IP为基础的不同层次上多个协议的集合,也成TCP/IP协议族、或TCP/IP协议栈
TCP:Transmission Control Protocol 传输控制协议
IP:Internet Protocol 互联网协议 - TCP/IP五层模型
应用层:HTTP、FTP、SMTP、Telnet等
传输层:TCP ,UDP,UGP
网络层: IP,ICMP,IGMP
数据链路层:ARP,RARP
物理层:网线、双绞线、网卡等 - IP地址
为实现网络中不同计算机之间的通信,每台计算机都必须有一个唯一的标识—IP地址。 - 端口
区分一台主机的多个不同应用程序,端口号范围为0-65535,其中0-1023位为系统保留。
如:HTTP:80 FTP:21 Telnet:23
IP地址+端口号组成了所谓的Socket,Socket是网络上运行的程序之间双向通信链路的终结点,是TCP和UDP的基础 - Socket套接字:
网络上具有唯一标识的IP地址和端口组合在一起才能构成唯一能识别的标识符套接字。
Socket原理机制:
通信的两端都有Socket
网络通信其实就是Socket间的通信
数据在两个Socket间通过IO传输 - Java中的网络支持
针对网络通信的不同层次,Java提供了不同的API,其提供的网络功能有四大类:
InetAddress:用于标识网络上的硬件资源,主要是IP地址
URL:统一资源定位符,通过URL可以直接读取或写入网络上的数据
Sockets:使用TCP协议实现的网络通信Socket相关的类
Datagram:使用UDP协议,将数据保存在用户数据报中,通过网络进行通信。
InetAdress
Java提供了InetAddress类来代表IP地址,实现对IP地址的封装。
获得本机InetAddress实例
InetAddress address = InetAddress.getLocalHost();
System.out.println(address.getHostName());
System.out.println(address.getHostAddress());
获得其他主机InetAddress实例
InetAddress address2 =InetAddress.getByName("其他主机名");
InetAddress address3 =InetAddress.getByName("IP地址");
URL
url是统一资源定位符,对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
构造函数
public URL(String spec)
spec - 将作为 URL 解析的 String。
public URL(String protocol, String host, int port,String file,URLStreamHandler handler)
protocol - 要使用的协议名称。
host - 主机名称。
port - 主机端口号。
file - 主机上的文件
handler - URL 的流处理程序。
不一定使用全部参数。
http://www.baidu.com
http:// :代表超文本传输协议,通知microsoft.com服务器显示Web页,通常不用输入;
www :代表一个Web(万维网)服务器;
baidu :为域名主体
.com :com为顶级域名,com是company的缩写,是最常用的顶级域名,表示商业网站。
常见的还有net,cn等
URL的常用方法
try {
URL url = new URL("https://mp.csdn.net/mdeditor/84201258");
System.out.println(url.getProtocol());//获取协议
System.out.println(url.getHost());//获取主机
System.out.println(url.getPort());//获取端口,如果未设置端口号,则返回 -1
System.out.println(url.getFile());//获取此 URL 的文件名。
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
使用URL读取网页内,通过URL对象的openStream()方法可以得到指定资源的输入流,通过流能够读取或访问网页上的资源
try {
URL url = new URL("http://www.baidu.com");
StringBuffer sBuffer =new StringBuffer();
InputStreamReader iReader =new InputStreamReader(url.openStream(),"UTF-8");
BufferedReader bReader =new BufferedReader(iReader);
String data ;
while((data=bReader.readLine())!=null){
sBuffer.append(data);
};
System.out.println(sBuffer.toString());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Tcp编程
1、TCP协议是面向连接的、可靠的、有序的、以字节流的方式发送数据,通过三次握手方式建立连接,形成传输数据的通道,在连接中进行大量数据的传输,效率会稍低。
2、Java中基于TCP协议实现网络通信的类
客户端的Socket类
服务器端的ServerSocket类
3、Socket通信的步骤
① 创建ServerSocket和Socket
② 打开连接到Socket的输入/输出流
③ 按照协议对Socket进行读/写操作
④ 关闭输入输出流、关闭Socket
4、服务器端:
① 创建ServerSocket对象,绑定监听端口
② 通过accept()方法监听客户端请求
③ 连接建立后,通过输入流读取客户端发送的请求信息
④ 通过输出流向客户端发送乡音信息
⑤ 关闭相关资源
客户端方法介绍
构造方法概述
Socket()
通过系统默认类型的 SocketImpl 创建未连接套接字
Socket(InetAddress address, int port)
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
address - IP 地址。
port - 端口号。
普通方法概述
void connect(SocketAddress endpoint)
将此套接字连接到服务器。
void setSoTimeout(int timeout)
启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。 当时间超过timeout毫秒
时,将触发InterruptedIOException异常,因此可以在try-catch块关闭套接字或再次
读写数据。
boolean isConnected()
返回套接字的连接状态。
注意:
(1)创建套接字时,应设置其超时值, 当时间超过超时值时,将触发InterruptedIOException异常,因此可以在try-catch块关闭套接字或再次读写数据。如果没有设置,它可能一直等待下去。
(2)通常将网络操作放在单独的线程中,并让它和程序的其他部分分开运行。
服务器端方法概述
构造函数
ServerSocket()
创建非绑定服务器套接字。
ServerSocket(int port)
创建绑定到特定端口的服务器套接字。
普通方法
Socket accept()
侦听并接受到此套接字的连接。
void bind(SocketAddress endpoint)
将 ServerSocket 绑定到特定地址(IP 地址和端口号)。
InetAddress getInetAddress()
返回此服务器套接字的本地地址。
void setSoTimeout(int timeout)
通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。
boolean isBound()
返回 ServerSocket 的绑定状态。
(1)创建服务器端时,要绑定端口号,可以在构造函数或者利用bind()函数绑定,服务器端监听这个端口,客户端通过IP地址和这个端口连接到服务器上。
(2)端口0-1023的用途由Internet地址分配机构使用。
(3)要先运行服务器端,在运行客户端。
实例:
创建客户端和服务端,客户端连接到服务器时,服务端发送当前时间,并断开连接,客户端显示出来时间。
服务器端代码
package CommDemo;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import javax.xml.crypto.Data;
public class ServerDemo extends Thread{
private ServerSocket myServer =null;
public ServerDemo(int port){
try {
myServer =new ServerSocket(port);
System.out.println("服务器开始运行");
} catch (IOException e) {
e.printStackTrace();
System.err.println("could not ....");
}
}
public void run(){
//对客户端的连接要放在多线程里,否则一直等待连接。
Socket mySocket =null;
while(true){
//死循环,不断检测有无连接的客户端
try {
mySocket =myServer.accept();
System.out.println("客户端连接成功");
BufferedOutputStream bOutputStream =new
BufferedOutputStream(mySocket.getOutputStream());
Date date = new Date();
PrintWriter pWriter =new PrintWriter(bOutputStream,false);
pWriter.println(date);
pWriter.flush();
pWriter.close();
bOutputStream.close();
mySocket.close();
} catch (IOException e) {
e.printStackTrace();
System.err.println("无法连接到客户端");
}
}
}
public static void main(String[] args) {
ServerDemo myServerDemo =new ServerDemo(6666);
myServerDemo.start();
}
}
客户端程序
package CommDemo;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class ClientDemo extends Thread implements ActionListener {
private Socket mySocket = null;
private JButton connect =new JButton("连接服务器");
private JButton disconnect =new JButton("断开服务器");
BufferedInputStream bInputStream =null;
JTextArea myArea =null;
public ClientDemo(){
//初始化界面
JFrame frame = new JFrame("客户端");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myArea=new JTextArea("",5,10);
frame.getContentPane().add(myArea, "Center");
JPanel panel =new JPanel();
panel.setLayout(new FlowLayout());
connect.addActionListener(this);
disconnect.addActionListener(this);
panel.add(connect);
panel.add(disconnect);
frame.getContentPane().add(panel, "South");
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
ClientDemo myClientDemo =new ClientDemo();
}
@Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==connect){
try {
StringBuffer sBuffer = new StringBuffer();
mySocket = new Socket("127.0.0.1", 6666);//连接本地IP的6666端口
mySocket.setSoTimeout(10000);//设置超时值
bInputStream=new BufferedInputStream(mySocket.getInputStream());
int data =-1;
while((data=bInputStream.read())!=-1){
sBuffer.append((char)data);
}
myArea.setText(sBuffer.toString());
} catch (IOException e1) {
e1.printStackTrace();
}
}else if(e.getSource()==disconnect){
try {
myArea.setText("已断开连接");
bInputStream.close();
mySocket.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
UDP编程
- UDP协议(用户数据报协议)是无连接的、不可靠的、无序的,速度快,进行数据传输时,首先将要传输的数据定义成数据报(Datagram),大小限制在64k,在数据报中指明数据索要达到的Socket(主机地址和端口号),然后再将数据报发送出去
- UDP通信中使用的两个类:
DatagramPacket类:表示数据报包
DatagramSocket类:进行端到端通信的类 - 使用UDP进行编程的步骤
客户端:
(1)使用DatagramPacket包装要发送的数据。
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。address是要发送的服务器的ip地址,port是服务器的端口号。
(2)建立DatagramSocket对象,通过方法DatagramSocket(int port)
创建数据报套接字并将其绑定到本地主机上的指定端口。这里的port是客户端的端口号,服务器可以根据客户端的IP和端口,向客户端反馈信息。
(3) void send(DatagramPacket p)
从此套接字发送数据报包 。
服务器端
(1)建立DatagramSocket对象,通过方法DatagramSocket(int port)
创建数据报套接字并将其绑定到本地主机上的指定端口。
(2)DatagramPacket(byte[] buf, int length)
构造 DatagramPacket,用来接收长度为 length 的数据包。
(3)使用DatagramSocket对象的 void receive(DatagramPacket p)
方法从此套接字接收数据报包。
(4)将此数据包转换成适当的数据形式
(5)通过int getPort()
返回客户端的端口。InetAddress getInetAddress()
返回客户端的IP地址。 从而服务端也可向客户端发送数据包,发送步骤等同于客户端发送数据包。
示例
客户端向服务器端发送当前时间,服务器端显示收到的时间。
运行结果
上面是两次客户端发来结果时,服务器端的显示
服务器端
package CommDemo;
import java.awt.BorderLayout;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import javax.swing.JFrame;
import javax.swing.JTextArea;
public class UdpServerDemo extends JFrame implements Runnable{
private DatagramSocket serverSocket = null;
private DatagramPacket receivePacket =null;
private byte bytes[];
JTextArea textArea =null;
public UdpServerDemo(int port){
//建立界面
super("服务器端");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
textArea=new JTextArea("",10,50);
getContentPane().add(textArea, "Center");
pack();
setVisible(true);
try {
System.out.println("服务器正在运行");
serverSocket =new DatagramSocket(port);
bytes =new byte[1024];
receivePacket =new DatagramPacket(bytes, bytes.length);
} catch (SocketException e) {
System.out.println("couldnot");
e.printStackTrace();
}
}
public void run(){
while(true){
try {
serverSocket.receive(receivePacket);
System.out.println("客户端已连接");
Object object =bytesToObject(receivePacket.getData());
textArea.append("客户端发来:"+object .toString()+"\n");
} catch (IOException e) {
System.out.println("连接失败");
e.printStackTrace();
}
}
}
//将字节数组转换成对象
public Object bytesToObject(byte bytes[]){
Object obj =null;
try {
ByteArrayInputStream bArrayInputStream =new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream =new
ObjectInputStream(bArrayInputStream);
obj =objectInputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return obj;
}
public static void main(String[] args) {
UdpServerDemo myServerDemo =new UdpServerDemo(6667);
Thread serverThread = new Thread(myServerDemo);
serverThread.start();
}
}
客户端
package CommDemo;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Date;
import javax.swing.JFrame;
public class UdpClientDemo {
DatagramSocket clientSocket =null;
DatagramPacket clientPacket =null;
byte bytes [];
InetAddress sendAddress =null;
public UdpClientDemo(int port){
try {
clientSocket =new DatagramSocket(port);
sendAddress = InetAddress.getByName("127.0.0.1");
bytes=new byte[1024];
bytes = ObjectToBytes(new Date());
clientPacket =new DatagramPacket(bytes,bytes.length,sendAddress,6667);
clientSocket.send(clientPacket);
clientSocket.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public byte[] ObjectToBytes(Object obj){
ByteArrayOutputStream bArrayOutputStream =new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream =new ObjectOutputStream(bArrayOutputStream);
objectOutputStream.writeObject(obj);
bytes =bArrayOutputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return bArrayOutputStream.toByteArray();
}
public static void main(String[] args) {
UdpClientDemo myClientDemo = new UdpClientDemo(6668);
}
}