一、网络基础知识
1、两台计算机要通过网络进行通信,必须具备:
1)唯一的标识(IP地址);
2)需要共同的语言(协议);
3)辨别不同应用程序(端口号)。
2、了解相关概念
1)IP地址:
每台计算机的唯一标识,用来区分网络中的不同主机,是两台主机进行网络通信必不可少的。2)协议:
(1)TCP/IP协议:目前世界上应用最为广泛的协议。是以TCP和IP为基础的不同层次上多个协议的集合。也称为:TCP/IP协议族 或者 TCP/IP协议栈。(TCP: Transmission Control Protocol -- 传输控制协议;IP :Internet Protocol -- 互联网协议)
3)CP/IP模型(网络分层):
(1)物理层(如网线、网线槽)
(2)数据链路层
(3)网络层
(4)传输层:TCP/IP协议
(5)应用层:HTTP超文本传输协议、FTP文件传输协议、SMTP简单邮件传送协议、Telnet远程登录服务。
4)端口号:
(1)用于区分不同的应用程序;
(2)端口号范围为0-65535,其中0-1023为系统所保留;
(3)IP地址和端口号组成了所谓的Socket,Socket是网络上运行的程序之间双向通信链路的终结点,是TCP和UDP的基础。
(4)常用端口号-- http: 80; ftp; 21; telnet; 23。
5)JAVA中得网络支持。针对网络通信的不同层次,Java提供的网络功能有四大类:
(1)InetAddress:用于标识网络上的硬件资源。(IP地址)
(2)URL:统一资源定位符---通过URL可以直接读取或写入网络上得数据。
(3)Sockets:使用TCP协议实现网络通信的Socket相关的类。
(4)Datagram:使用UDP协议,将数据保存在数据报中,通过网络进行通信。
二、Java网络相关的API的应用
1、InetAddress类
1)InetAddress类没有构造方法,所以不能直接new出一个对象;但可以通过InetAddress类的静态方法获得InetAddress的对象:如
InetAddress.getLocalHost();
InetAddress.getByName();
2)类主要方法:
String - address.getHostName();
String - address.getHostAddress();
public static InetAddress getByName(String host) throws UnknownHostException
// 在给定主机名的情况下确定主机的 IP 地址。主机名可以是机器名(如 "java.sun.com"),也可以是其 IP 地址的文本表示形式
3)InetAddress使用示例代码:
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
/**
* InetAddress类的基本使用
*/
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
/*
* 获取本地主机信息
*/
InetAddress address = InetAddress.getLocalHost();
System.out.println("主机名称" + address.getHostName());
System.out.println("IP地址:" + address.getHostAddress());
byte[] bytes = address.getAddress(); // 以获取字节数组的形式获取地址
System.out.println("自己数组形式的ip: " + Arrays.toString(bytes));
System.out.println(address);// 直接输出InetAddress对象
System.out.println();
/*
* 根据机器名获取InetAddresss实例
*/
InetAddress address2 = InetAddress.getByName("Moonlight");
System.out.println("计算机名:" + address2.getHostName());
System.out.println("IP地址:" + address2.getHostAddress());
System.out.println();
/*
* 根据IP获取InetAddress实例
*/
InetAddress address3 = InetAddress.getByName("192.168.1.102");
System.out.println("计算机名:" + address3.getHostName());
System.out.println("IP地址:" + address3.getHostAddress());
System.out.println();
/*
* 获取其它地址
*/
InetAddress address4 = InetAddress.getByName("baidu.com");
System.out.println("计算机名:" + address4.getHostName());
System.out.println("IP地址:" + address4.getHostAddress());
System.out.println();
}
}
2、URL类
1)简介:
(1)URL(Uniform Resource Locator)统一资源定位符,表示Internet上某一资源的地址
(2)URL由两部分组成:协议名称和资源名称,中间用冒号隔开
(3)在java.net包中,提供了URL类来表示URL
2)使用URL读取网页内容
(1)通过URL对象的openStream()方法可以得到指定资源的输入流
(2)通过输入流可以读取、访问网络上的数据
3)URL类使用示例代码:
import java.net.MalformedURLException;
import java.net.URL;
/**
* URL常用方法
*/
public class URLDemo {
public static void main(String[] args) throws MalformedURLException {
// 创建URL实例
URL url = new URL("http://www.baidu.com");
URL url2 = new URL(url, "/index.html?uname=1#test"); // 在原有的URL资源上再创建URL实例,#后面表示锚点
System.out.println("协议" + url2.getProtocol());
System.out.println("主机" + url2.getHost());
// 如果未指定端口号,则使用默认的端口号,此时getPort()返回-1
System.out.println("端口" + url2.getPort());
System.out.println("文件路径" + url2.getPath());
System.out.println("文件名称:" + url2.getFile());
System.out.println("相对路径" + url2.getRef());
System.out.println("查询字符串" + url2.getQuery());
}
}
三、Socket实现TCP编程
1、Socket通信模型
1)上图的通信模型的顺序为:
(1)先建立服务端监听Soket,等待客户端请求。
(2)客户端创建socket,向服务端发送请求。
(3)服务端接收请求,并创建连接socket到此步骤时,服务端与客户端建立了连接。
(4)服务端和客户端开始通信(InputStream、OutputStream)。
(5)最后关闭socket与相关资源。
2、Socket实现TCP编程通信的实现步骤
1)服务端:
(1)创建ServerSocket对象,绑定监听端口
(2)通过accept()方法监听客户端的请求
(3)建立连接后,通过输入流读取客户端发送的请求信息
(4)通过输出流向客户端发送相应信息
(5)关闭相关资源
代码示例:
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务端
* 基于TCP协议的Socket通信 -- 单线程
*/
public class TCPServer {
public static void main(String[] args) {
try {
// 1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听
ServerSocket serverSocket = new ServerSocket(8888);
// 2.调用accept()方法开始监听,等待客户端的连接
System.out.println("服务器即将启动,等待客户端的连接***");
Socket socket = serverSocket.accept();
// 3.获取输入流,并读取客户端信息
InputStream is = socket.getInputStream();
InputStreamReader irs = new InputStreamReader(is);
BufferedReader br = new BufferedReader(irs);
String info = null;
while((info=br.readLine()) != null){
System.out.println("服务器接收到消息:" + info);
}
socket.shutdownInput();//关闭输入流
// 4.获取输出流,响应客户端请求
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);// 包装为打印流
pw.write("终于等到你!");
pw.flush();
// 5.关闭资源
pw.close();
os.close();
br.close();
irs.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2)客户端:
(1)创建Socket对象,指明需要连接的服务器的地址和端口号
(2)连接建立后,通过输出流向服务器端发送请求信息
(3)通过输入流获取服务器相应的信息
(4)关闭相关资源
代码示例:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
/**
* 客户端
* 基于TCP协议的Socket通信
*/
public class TCPClient {
public static void main(String[] args) {
try {
// 1.创建客户端Socket,指定服务器地址和端口
Socket socket = new Socket("localhost", 8888);
// 2.获取输出流,向服务器端发送信息
OutputStream os = socket.getOutputStream();//字节输出流
PrintWriter pw = new PrintWriter(os);
pw.write("客户端发信息来啦~~");
pw.flush();
socket.shutdownOutput();// 关闭输出流
// 3.获取输入流,并读取服务器端的响应信息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
while((info=br.readLine()) != null){
System.out.println("客户端接收到信息:" + info);
}
// 4.关闭资源
br.close();
is.close();
pw.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3、应用多线程来实现服务器与多客户端之间的通信的基本基本步骤
(1)服务器端创建ServerSocket,循环调用accept()等待客户端连接
(2)客户端创建一个socket并请求和服务器端连接
(3)服务器端接收客户端请求,创建socket与该客户建立专线连接
(4)建立连接的两个socket在一个单独的线程上对话
(5)服务器端继续等待新的连接
代码示例:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务端
* 基于TCP协议的Socket通信 -- 多线程实现多客户的通信
*/
public class TCPServerMulConnection {
public static void main(String[] args) {
try {
// 创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听
ServerSocket serverSocket = new ServerSocket(8888);
// 调用accept()方法开始监听,等待客户端的连接
System.out.println("服务器即将启动,等待客户端的连接***");
Socket socket = null;
int count = 0; // 记录客户端的连接
// 循环监听等待客户端的连接
while (true) {
// 调用accept()方法开始监听,等待客户端的连接
socket = serverSocket.accept();
// 创建一个新的线程
ServerThreadHelper serverThread = new ServerThreadHelper(socket);
//serverThread.setPriority(4); //可设置线程优先级(优先级默认为5,数字越小,优先级越低;未设置优先级可能导致运行时速度非常慢,可适当降低)
// 启动线程
serverThread.start();
count++; // 统计客户端的数量
InetAddress address = socket.getInetAddress();
System.out.println("第 " + count + " 个客户端连接成功!此客户端的ip为:" + address.getHostAddress());
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 服务器端线程处理类
*/
class ServerThreadHelper extends Thread{
// 和本线程相关的socket
Socket socket = null;
public ServerThreadHelper(Socket socket){
this.socket = socket;
}
// 线程执行的操作,响应客户端请求
public void run(){
InputStream is = null;
InputStreamReader irs = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
try {
// 1.获取输入流,并读取客户端信息
is = socket.getInputStream();
irs = new InputStreamReader(is);
br = new BufferedReader(irs);
String info = null;
while((info=br.readLine()) != null){
System.out.println("服务器接收到消息:" + info);
}
socket.shutdownInput();//关闭输入流
// 2.获取输出流,响应客户端请求
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.write("终于等到你!");
pw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3.关闭资源
try {
if (pw!=null) {
pw.close();
}
if (os!=null) {
os.close();
}
if (br!=null) {
br.close();
}
if (irs!=null) {
irs.close();
}
if (is!=null) {
is.close();
}
if (socket!=null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
四、Socket实现UDP编程
1、UDP编程简介
(1)UDP协议(用户数据报协议)是无连接、不可靠、无序的,特点是速度比较快
(2)进行数据传输时,首先要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的Socket(主机地址和端口号),然后再将数据报发送出去
(3)相关的操作类:DatagramPacket:表示数据报包 DatagramSocket:进行端到端通信的类
2、Socket实现UDP编程
1)服务器端实现步骤
(1)创建DatagramSocket,指定端口号
(2)创建DatagramPacket,用于接收客户端请求
(3)接收客户端发送的数据信息
(4)读取数据
(5)关闭资源
示例代码:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* 服务端
* 基于UDP实现Socket通信
*/
public class UDPServer {
public static void main(String[] args) throws IOException {
/*
* 接收客户发送的数据
*/
// 1.创建服务器端DatagramSocket,指定端口
DatagramSocket socket = new DatagramSocket(8800);
// 2.创建数据报,用于接收客户端发送的数据
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
// 3.接收客户端发送的数据
System.out.println("服务器已启动,等待接收数据报中……");
socket.receive(packet); // 此方法在接收到数据报之前会一直阻塞
// 4.读取数据
String info = new String(data, 0, packet.getLength());
System.out.println("服务器接收到了新信息:" + info);
/*
* 向客户端响应数据
*/
// 1.定义
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "服务器已经收到信息啦~".getBytes();
// 2.创建数据报,包含响应的数据信息
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
// 3.发送数据,返回給客户端
socket.send(packet2);
// 4.关闭资源
socket.close();
}
}
2)客户端实现步骤
(1)定义发送信息
(2)创建DatagramPacket,包含将要发送的信息
(3)创建DatagramSocket
(4)发送数据(通过DatagramSocket发送)
(5)关闭资源
示例代码:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 客户端
* 基于UDP实现Socket通信
*/
public class UDPClient {
public static void main(String[] args) throws IOException {
/*
* 发送消息给服务端
*/
// 1.定义服务器的地址、端口号、数据报
InetAddress address = InetAddress.getByName("localhost");
int port = 8800;
byte[] data = "你好!我是客户端。".getBytes();
// 2.创建数据报,包含发送的数据信息
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
// 3.创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket();
// 4.向服务器端发送数据报
socket.send(packet);
/*
* 接收服务端响应
*/
// 1.创建数据报,用于接收服务器端响应的数据
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
// 2.接收服务器响应的数据
socket.receive(packet2);
// 3.读取数据
String reply = new String(data2, 0, packet2.getLength());
System.out.println("客户端接收到新信息:" + reply);
// 4.关闭资源
}
}
五、Socket实现UDP编程
1、使用多线程编写网络程序时,可以将线程的优先级降低,线程的优先级默认是五,数字越小,优先级越低;未设置优先级可能导致运行时速度非常慢,可适当降低。
2、对于同一个socket,如果关闭了输出流,则与该输出流相关的socket也会被关闭,所以一般不用关闭流,直接关闭socet即可。
3、可以将一个对象序列化之后,通过流的方式在网络上传输
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(User);
4、网络上传输文件,也是将文件转换为流,通过流的方式在网络上传输
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(new File("d://")));