文章目录
Java 网络编程
网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。
java.net 包中 J2SE 的 API 包含有类和接口,它们提供低层次的通信细节。你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。
java.net 包中提供了两种常见的网络协议的支持:
- TCP:TCP(英语:Transmission Control Protocol,传输控制协议) 是一种面向连接的、可靠的、基于字节流的传输层通信协议,TCP 层是位于 IP 层之上,应用层之下的中间层。TCP 保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。
- UDP:UDP (英语:User Datagram Protocol,用户数据报协议),位于 OSI 模型的传输层。一个无连接的协议。提供了应用程序之间要发送数据的数据报。由于UDP缺乏可靠性且属于无连接协议,所以应用程序通常必须容许一些丢失、错误或重复的数据包。
1、Socket编程(TCP)
套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。
当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。
java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。
以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:
- 服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。
- 服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
- 服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。
- Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。
- 在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。
连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。
TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.以下是一些类提供的一套完整的有用的方法来实现 socket。
ServerSocket 类的方法
服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求。
ServerSocket 类有四个构造方法:
构造方法 | 方法描述 |
---|---|
public ServerSocket(int port) throws IOException | 创建绑定到特定端口的服务器套接字。 |
public ServerSocket(int port, int backlog) throws IOException | 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 |
public ServerSocket(int port, int backlog, InetAddress address) throws IOException | 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。 |
public ServerSocket() throws IOException | 创建非绑定服务器套接字。 |
创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。
这里有一些 ServerSocket 类的常用方法:
方法 | 方法描述 |
---|---|
public int getLocalPort() | 返回此套接字在其上侦听的端口。 |
public Socket accept() throws IOException | 侦听并接受到此套接字的连接。 |
public void setSoTimeout(int timeout) | 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。 |
public void bind(SocketAddress host, int backlog) | 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。 |
Socket 类的方法
java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。
Socket 类有五个构造方法:
构造方法 | 方法描述 |
---|---|
public Socket(String host, int port) throws UnknownHostException, IOException | 创建一个流套接字并将其连接到指定主机上的指定端口号。 |
public Socket(InetAddress host, int port) throws IOException | 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 |
public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException | 创建一个套接字并将其连接到指定远程主机上的指定远程端口。 |
public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException | 创建一个套接字并将其连接到指定远程地址上的指定远程端口。 |
public Socket() | 通过系统默认类型的 SocketImpl 创建未连接套接字 |
当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。
下面列出了一些感兴趣的方法,注意客户端和服务器端都有一个 Socket 对象,所以无论客户端还是服务端都能够调用这些方法。
方法 | 方法描述 |
---|---|
public void connect(SocketAddress host, int timeout) throws IOException | 将此套接字连接到服务器,并指定一个超时值。 |
public InetAddress getInetAddress() | 返回套接字连接的地址。 |
public int getPort() | 返回此套接字连接到的远程端口。 |
public int getLocalPort() | 返回此套接字绑定到的本地端口。 |
public SocketAddress getRemoteSocketAddress() | 返回此套接字连接的端点的地址,如果未连接则返回 null。 |
public InputStream getInputStream() throws IOException | 返回此套接字的输入流。 |
public OutputStream getOutputStream() throws IOException | 返回此套接字的输出流。 |
public void close() throws IOException | 关闭此套接字。 |
InetAddress 类的方法
这个类表示互联网协议(IP)地址。下面列出了 Socket 编程时比较有用的方法:
方法 | 方法描述 |
---|---|
static InetAddress getByAddress(byte[] addr) | 在给定原始 IP 地址的情况下,返回 InetAddress 对象。 |
static InetAddress getByAddress(String host, byte[] addr) | 根据提供的主机名和 IP 地址创建 InetAddress。 |
static InetAddress getByName(String host) | 在给定主机名的情况下确定主机的 IP 地址。 |
String getHostAddress() | 返回 IP 地址字符串(以文本表现形式)。 |
String getHostName() | 获取此 IP 地址的主机名。 |
static InetAddress getLocalHost() | 返回本地主机。 |
String toString() | 将此 IP 地址转换为 String。 |
TCP实现文件上传功能
客户端:
//文件从客户端上传到服务端
public class FileUploadClient {
public static void main(String[] args) throws Exception {
//1、要先知道服务器的地址,端口号
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 8888;
//2、创建一个Socket连接
Socket socket = new Socket(ip, port);
//3、创建一个输出流
OutputStream os = socket.getOutputStream();
//4、读取文件
FileInputStream fis = new FileInputStream(new File("src//image.png"));
//5、写文件
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1){
os.write(buffer, 0, len);
}
//通知服务器,已传输完毕
socket.shutdownOutput();//关闭客户端的输出流
//确定服务器接收完毕,才能够断开连接
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[1024];
int len2;
while ((len2 = is.read(buffer2)) != -1){
baos.write(buffer2, 0, len2);
}
System.out.println(baos.toString());
//5、关闭资源
baos.close();
is.close();
fis.close();
os.close();
socket.close();
}
}
服务端:
//服务端接收客户端上传的文件
public class FileUploadServer {
public static void main(String[] args) throws Exception {
//1、创建服务器地址
ServerSocket serverSocket = new ServerSocket(8888);
//2、等待客户端连接进来
Socket socket = serverSocket.accept();
//3、获取输入流
InputStream is = socket.getInputStream();
//4、文件输出
FileOutputStream fos = new FileOutputStream(new File("d://image.png"));
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1){
fos.write(buffer, 0, len);
}
//通知客户端我接收完毕了
OutputStream os = socket.getOutputStream();
os.write("我已接收完毕,你可以断开连接了".getBytes());
//关闭资源
os.close();
fos.close();
is.close();
socket.close();
serverSocket.close();
}
}
2、UDP通信
多线程实现两用户通信:
发送消息动作:
public class TalkSend implements Runnable {
private DatagramSocket socket = null;
private int fromPoint;
private String toIP;
private int toPoint;
public TalkSend(int fromPoint, String toIP, int toPoint) {
this.fromPoint = fromPoint;
this.toIP = toIP;
this.toPoint = toPoint;
try {
socket = new DatagramSocket(fromPoint);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
try {
//1、从控制台拿到需要发送的数据
Scanner in = new Scanner(System.in);
String data = in.nextLine();
byte[] buffer = data.getBytes();
//2、将数据封装成数据包,用于socket传送
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, new InetSocketAddress(toIP, toPoint));
//3、发送
socket.send(packet);
//4、如果此次发送的是bye,就关闭通信
if(data.equals("bye"))
break;
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
接收消息动作:
public class TalkReceive implements Runnable {
private int myPoint;
private DatagramSocket socket = null;
private String fromMsg = null;
public TalkReceive(int myPoint, String fromMsg) {
this.myPoint = myPoint;
this.fromMsg = fromMsg;
try {
socket = new DatagramSocket(myPoint);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (true){
try {
//1、准备接受的包裹
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container,0, container.length);
//2、接受数据并放入数据包中
socket.receive(packet);
//3、拿出数据包中的数据
byte[] data = packet.getData();
String receiveData = new String(data, 0, data.length);
//4、打印数据
System.out.println(fromMsg + ":" + receiveData);
//4、如果对方发送的是bye,就关闭通信
if(receiveData.equals("bye"))
break;
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
用户一:
public class chatOne {
public static void main(String[] args) {
new Thread(new TalkSend(7777, "localhost", 6666)).start();
new Thread(new TalkReceive(9999, "Two")).start();
}
}
用户二;
public class charTwo {
public static void main(String[] args) {
new Thread(new TalkSend(8888, "localhost", 9999)).start();
new Thread(new TalkReceive(6666, "One")).start();
}
}
3、URL处理
URL(Uniform Resource Locator)中文名为统一资源定位符,有时也被俗称为网页地址。表示为互联网上的资源,如网页或者 FTP 地址。
URL 可以分为如下几个部分。
protocol://host:port/path?query#fragment
protocol(协议)可以是 HTTP、HTTPS、FTP 和 File,port 为端口号,path为文件路径及文件名。
HTTP 协议的 URL 实例如下:
http://www.baidu.com/index.html?language=cn#j2se
URL 解析:
- **协议为(protocol):**http
- 主机为(host:port):www.baidu.com
- 端口号为(port): 80 ,以上URL实例并未指定端口,因为 HTTP 协议默认的端口号为 80。
- 文件路径为(path):/index.html
- 请求参数(query):language=cn
- **定位位置(fragment):**j2se,定位到网页中 id 属性为 j2se 的 HTML 元素位置 。
URL 类方法
在java.net包中定义了URL类,该类用来处理有关URL的内容。对于URL类的创建和使用,下面分别进行介绍。
java.net.URL提供了丰富的URL构建方式,并可以通过java.net.URL来获取资源。
构造方法 | 方法描述 |
---|---|
public URL(String protocol, String host, int port, String file) throws MalformedURLException | 通过给定的参数(协议、主机名、端口号、文件名)创建URL。 |
public URL(String protocol, String host, String file) throws MalformedURLException | 使用指定的协议、主机名、文件名创建URL,端口使用协议的默认端口。 |
public URL(String url) throws MalformedURLException | 通过给定的URL字符串创建URL |
public URL(URL context, String url) throws MalformedURLException | 使用基地址和相对URL创建 |
URL类中包含了很多方法用于访问URL的各个部分,具体方法及描述如下:
方法 | 方法描述 |
---|---|
public String getPath() | 返回URL路径部分。 |
public String getQuery() | 返回URL查询部分。 |
public String getAuthority() | 获取此 URL 的授权部分。 |
public int getPort() | 返回URL端口部分 |
public int getDefaultPort() | 返回协议的默认端口号。 |
public String getProtocol() | 返回URL的协议 |
public String getHost() | 返回URL的主机 |
public String getFile() | 返回URL文件名部分 |
public String getRef() | 获取此 URL 的锚点(也称为"引用")。 |
public URLConnection openConnection() throws IOException | 打开一个URL连接,并运行客户端访问资源。 |
测试URL方法
public class URLDemo
{
public static void main(String [] args)
{
try
{
URL url = new URL("http://www.baidu.com:8080/index.html?language=cn#j2se");
System.out.println("URL 为:" + url.toString());
System.out.println("协议为:" + url.getProtocol());
System.out.println("验证信息:" + url.getAuthority());
System.out.println("文件名及请求参数:" + url.getFile());
System.out.println("主机名:" + url.getHost());
System.out.println("路径:" + url.getPath());
System.out.println("端口:" + url.getPort());
System.out.println("默认端口:" + url.getDefaultPort());
System.out.println("请求参数:" + url.getQuery());
System.out.println("定位位置:" + url.getRef());
}catch(IOException e)
{
e.printStackTrace();
}
}
}
以上实例编译运行结果如下:
URL 为:http://www.baidu.com:8080/index.html?language=cn#j2se
协议为:http
验证信息:www.baidu.com:8080
文件名及请求参数:/index.html?language=cn
主机名:www.baidu.com
路径:/index.html
端口:8080
默认端口:80
请求参数:language=cn
定位位置:j2se
URLConnections 类方法
openConnection() 返回一个 java.net.URLConnection。
例如:
- 如果你连接HTTP协议的URL, openConnection() 方法返回 HttpURLConnection 对象。
- 如果你连接HTTPS协议的URL, openConnection() 方法返回 HttpsURLConnection 对象。
- 如果你连接的URL为一个 JAR 文件, openConnection() 方法将返回 JarURLConnection 对象。
- 等等…
URLConnection 方法列表如下:
方法 | 方法描述 |
---|---|
Object getContent() | 检索URL链接内容 |
Object getContent(Class[] classes) | 检索URL链接内容 |
String getContentEncoding() | 返回头部 content-encoding 字段值。 |
int getContentLength() | 返回头部 content-length字段值 |
String getContentType() | 返回头部 content-type 字段值 |
int getLastModified() | 返回头部 last-modified 字段值。 |
long getExpiration() | 返回头部 expires 字段值。 |
long getIfModifiedSince() | 返回对象的 ifModifiedSince 字段值。 |
public void setDoInput(boolean input) | URL 连接可用于输入和/或输出。如果打算使用 URL 连接进行输入,则将 DoInput 标志设置为 true;如果不打算使用,则设置为 false。默认值为 true。 |
public void setDoOutput(boolean output) | URL 连接可用于输入和/或输出。如果打算使用 URL 连接进行输出,则将 DoOutput 标志设置为 true;如果不打算使用,则设置为 false。默认值为 false。 |
public InputStream getInputStream() throws IOException | 返回URL的输入流,用于读取资源 |
public OutputStream getOutputStream() throws IOException | 返回URL的输出流, 用于写入资源。 |
public URL getURL() | 返回 URLConnection 对象连接的URL |
使用URL下载网络资源
public class UrlTest {
public static void main(String[] args) throws IOException {
//1、创建资源的URL
URL url = new URL("https://www.baidu.com/img/pc_10cd458fd1807b67b2ba322e4cf973e1.gif");
//2、根据URL拿到连接
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
//3、从连接中拿到输入流
InputStream is = connection.getInputStream();
//4、下载该资源到本地
FileOutputStream fos = new FileOutputStream(new File("d://baidu.gif"));
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1)
fos.write(buffer, 0, len);
}
}