-
获取本机InetAddress对象getLocalHost
-
根据指定主机名/域名获取IP地址对象getByName
-
获取InetAddress对象的主机名getHostName
-
获取InetAddress对象的地址getHostAddress
应用案例
编写代码,获取计算机的主机名和 IP 地址相关 API
public class INetAddressTest {
public static void main(String[] args) {
try {
//获取本机 InetAddress 对象 getLocalHost
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
//根据指定主机名/域名获取 ip 地址对象 getByName
InetAddress host2 = InetAddress.getByName("LAPTOP-SJB7BAG5");
System.out.println(host2);
InetAddress host3 = InetAddress.getByName("www.baidu.com");
System.out.println(host3);
//获取 InetAddress 对象的主机名 getHostName
String host3Name = host3.getHostName();
System.out.println(host3Name);
//获取 InetAddress 对象的地址 getHostAddress
String host3Address = host3.getHostAddress();
System.out.println(host3Address);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
Socket
- 套接字(socket)开发网络应用程序被广泛应用,以至于成为事实上的标准
- 通信的两端都要有Socket,是两台机器通信的端点
- 网络通信其实就是Socket间的通信
- Socket允许把网络连接当成一个流,数据在两个Socket间通过IO传输。
- 一般主动发起通信的应用程序属于客户端,等待通信请求的为服务端
- 基于客户端——服务端的网络通信
- 底层使用的是TCP/IP 协议
- 应用场景案例:客户端发送数据,服务端接受并显示控制台
- 基于Socket的TCP编程
- 编写一个服务器端和一个客户器端
- 服务器端在9999端口监听
- 客户端连接到服务端,发送"hello, server",并接收服务端回发的"hello,client",,再退出
- 服务端接收到客户端发送的信息,输出,并发送"hello, client",再退出
✏️SocketTCP01Server.java
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//思路
//1. 在本机 的 9999 端口监听, 等待连接
// 细节: 要求在本机没有其它服务在监听 9999
// 细节:这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个客户端连接服务器的并发]
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在 9999 端口监听,等待连接..");
//2. 当没有客户端连接 9999 端口时,程序会 阻塞, 等待连接
// 如果有客户端连接,则会返回 Socket 对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("服务端 socket =" + socket.getClass());
//
//3. 通过 socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
InputStream inputStream = socket.getInputStream();
//4. IO 读取
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
System.out.println(new String(buf, 0, readLen));//根据读取到的实际长度,显示内容.
}
//5. 获取 socket 相关联的输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello, client".getBytes());
// 设置结束标记
socket.shutdownOutput();
outputStream.close();
//6.关闭流和 socket
inputStream.close();
socket.close();
serverSocket.close();//关闭
}
}
✏️SocketTCP01Client.java
public class SocketTCP01Client {
public static void main(String[] args) throws IOException {
//思路
//1. 连接服务端 (ip , 端口)
//解读: 连接本机的 9999 端口, 如果连接成功,返回 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端 socket 返回=" + socket.getClass());
//2. 连接上后,生成 Socket, 通过 socket.getOutputStream()
// 得到 和 socket 对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3. 通过输出流,写入数据到 数据通道
outputStream.write("hello, server".getBytes());
// 设置结束标记
socket.shutdownOutput();
//4. 获取和 socket 关联的输入流. 读取数据(字节),并显示
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf)) != -1) {
System.out.println(new String(buf, 0, readLen));
}
//5. 关闭流对象和 socket, 必须关闭
inputStream.close();
outputStream.close();
socket.close();
System.out.println("客户端退出.....");
}
}
(字符流)
✏️SocketTCP03Server.java
public class SocketTCP03Server {
public static void main(String[] args) throws IOException {
//思路
//1. 在本机 的 9999 端口监听, 等待连接
//细节: 要求在本机没有其它服务在监听 9999
//细节:这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个客户端连接服务器的并发]
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,在 9999 端口监听,等待连接..");
//2. 当没有客户端连接 9999 端口时,程序会 阻塞, 等待连接
//如果有客户端连接,则会返回 Socket 对象,程序继续
Socket socket = serverSocket.accept();
System.out.println("服务端 socket =" + socket.getClass());
//
//3. 通过 socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
InputStream inputStream = socket.getInputStream();
//4. IO 读取, 使用字符流, 老师使用 InputStreamReader 将 inputStream 转成字符流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();
System.out.println(s);//输出
//5. 获取 socket 相关联的输出流
OutputStream outputStream = socket.getOutputStream();
//使用字符输出流的方式回复信息
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write("hello client 字符流");
bufferedWriter.newLine();// 插入一个换行符,表示回复内容的结束
bufferedWriter.flush();//注意需要手动的 flush
//6.关闭流和 socket
bufferedWriter.close();
bufferedReader.close();
socket.close();
serverSocket.close();//关闭
}
}
✏️SocketTCP03Client.java
public class SocketTCP03Client {
public static void main(String[] args) throws IOException {
//思路
//1. 连接服务端 (ip , 端口)
//解读: 连接本机的 9999 端口, 如果连接成功,返回 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端 socket 返回=" + socket.getClass());
//2. 连接上后,生成 Socket, 通过 socket.getOutputStream()
//得到 和 socket 对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3. 通过输出流,写入数据到 数据通道, 使用字符流
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write("hello, server 字符流");
bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束, 注意,要求对方使用 readLine()!!!!
bufferedWriter.flush();// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道
//4. 获取和 socket 关联的输入流. 读取数据(字符),并显示
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();
System.out.println(s);
//5. 关闭流对象和 socket, 必须关闭
bufferedReader.close();//关闭外层流
bufferedWriter.close();
socket.close();
System.out.println("客户端退出.....");
}
}
应用案例
- 编写一个服务端,和一个客户端.服务器端在9999端口监听
- 客户端连接到服务端,发送一张图片e:\qie.png
- 服务器端接收到客户端发送的图片,保存到src下,发送"收到图片”再退出
- 客户端接收到服务端发送的“收到图片”,再退出
- 该程序要求使用StreamUtils.java,我们直接使用
StreamUtils.java(工具类)
public class StreamUtils {
public static byte[] streamToByteArray(InputStream is) throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
byte[] b = new byte[1024];
int len;
while((len=is.read(b))!=-1){
bos.write(b, 0, len);
}
byte[] array = bos.toByteArray();
bos.close();
return array;
}
public static String streamToString(InputStream is) throws Exception{
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder= new StringBuilder();
String line;
while((line=reader.readLine())!=null){ //当读取到 null 时,就表示结束
builder.append(line+"rn");
}
return builder.toString();
}
}
✏️TCPFileUploadServer.java(文件上传的服务端)
public class TCPFileUploadServer {
public static void main(String[] args) throws Exception {
//1. 服务端在本机监听 9999 端口
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端在 9999 端口监听....");
//2. 等待连接
Socket socket = serverSocket.accept();
//3. 读取客户端发送的数据
//通过 Socket 得到输入流
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);
//4. 将得到 bytes 数组,写入到指定的路径,就得到一个文件了
String destFilePath = "src\abc.jpg";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
bos.write(bytes);
bos.close();
// 向客户端回复 "收到图片"
// 通过 socket 获取到输出流(字符)
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
writer.write("收到图片");
writer.flush();//把内容刷新到数据通道
socket.shutdownOutput();//设置写入结束标记
//关闭其他资源
writer.close();
bis.close();
socket.close();
serverSocket.close();
}
}
✏️TCPFileUploadClient.java(文件上传的客户端)
public class TCPFileUploadClient {
public static void main(String[] args) throws Exception {
//客户端连接服务端 9999,得到 Socket 对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
//创建读取磁盘文件的输入流
//String filePath = "e:\qie.png";
String filePath = "D:\abc1.jpg";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
//bytes 就是 filePath 对应的字节数组
byte[] bytes = StreamUtils.streamToByteArray(bis);
//通过 socket 获取到输出流, 将 bytes 数据发送给服务端
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(bytes);//将文件对应的字节数组的内容,写入到数据通道
bis.close();
socket.shutdownOutput();//设置写入数据的结束标记
//=====接收从服务端回复的消息=====
InputStream inputStream = socket.getInputStream();
//使用 StreamUtils 的方法,直接将 inputStream 读取到的内容 转成字符串
String s = StreamUtils.streamToString(inputStream);
System.out.println(s);
//关闭相关的流
inputStream.close();
bos.close();
socket.close();
}
}
netstat指令
- netstat -an 可以查看当前主机网络情况,包括端口监听情况和网络连接情况
- netstat -an l more 可以分页显示
文件上传原理
当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是TCP/IP来分配的,是不确定的,是随机的。
用户聊天案例
聊天服务端
public class ChatServer {
// 定义了一个客户端Socket的集合对象
static List list = new ArrayList<>();
public static void main(String[] args) {
try {
// 在9999端口之上注册并创建ServerSocket对象
ServerSocket ss = new ServerSocket(9999);
System.out.println("server started...");
// 保持客户端与服务器端的“长连接”
while(true) {
// 通过ServerSocket对象的accept()方法得到Socket对象
Socket s = ss.accept();
// 将该Socket对象放入Socket集合中
list.add(s);
System.out.println("a clint is visitting...");
// 创建了服务器端的线程对象
ServerThread st = new ServerThread(s);
// 开启服务器端的线程对象
st.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ServerThread extends Thread{
private Socket s;
// 借助构造器将Socket对象传入
public ServerThread(Socket s) {
this.s = s;
}
@Override
public void run() {
try {
// 获取Socket对象的输入流对象
InputStream in = s.getInputStream();
// 创建BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(in));
// 通过socket对象的getOutputStream()的流对象创建PrintWriter对象
PrintWriter pw = new PrintWriter(s.getOutputStream());
// 只要用户不输入exit,就可以一直“聊天”
while(true) {
// 获取用户的输入行内容
String msg = br.readLine();
// 如果用户输入了exit,代表要退出
if("exit".equalsIgnoreCase(msg)) {
// 从socket集合中删除该socket对象
ChatServer.list.remove(s);
// 循环终止
break;
}
// 将用户输入的信息添加上时间内容
msg = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()) + "说: " + msg;
// 遍历整个ChatServer中的list
for(Socket s: ChatServer.list) {
// 得到用户的输入内容
pw = new PrintWriter(s.getOutputStream());
// 打印用户的输入内容
pw.println(msg);
// 刷新流对象
pw.flush();
}
}
pw.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
聊天客户端
public class ChatClient {
public static void main(String[] args) {
// 使用键盘输入来创建BufferedReader对象
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try {
// 让Socket连接指定主机和端口的ServerSocket
Socket s = new Socket("127.0.0.1", 9999);
// 创建ClientThread对象
ClientThread ct = new ClientThread(s);
// 开启ClientThread对象
ct.start();
// 通过socket对象的getOutputStream()得到输出流对象
OutputStream os = s.getOutputStream();
// 使用输出流对象创建PrintWriter对象
PrintWriter pw = new PrintWriter(os);
while(true) {
System.out.println("pls say something...");
String msg = br.readLine();
// 使用PrintWriter对象来打印用户输入的内容
pw.println(msg);
// 刷新PrintWrite对象
pw.flush();
// 用户输入了exit,则此案成终止,循环结束
if("exit".equalsIgnoreCase(msg)) {
ct.stop();
break;
}
}
pw.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class ClientThread extends Thread{
private Socket s;
public ClientThread(Socket s) {
this.s = s;
}
@Override
public void run() {
try {
// 通过Socket对象的getInputStream()的流对象创建BufferedReader对象
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
while(true) {
// 读取用户输入的一整行信息
String msg = br.readLine();
// 打印信息
System.out.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
项目开发流程
- 需求分析
- 需求分析师:懂技术+行业(银行、外贸、税务)
- 出一个需求分析报告(白皮书)该项目功能,客户具体要求
- 设计阶段
- 架构师/项目经理;
- 设计工作(UML类图,流程图,模块设计,数据库,架构);
- 原型开发;
- 组建团队;
- 实现阶段
- 程序员/码农;
- 完成架构师的模块功能;
- 测试自己的模块;
- 测试阶段
- 测试工程师
- 单元测试,测试用例,白盒测试,黑盒测试,集成测试
- 实施阶段
- 实施工程师(开发能力/环境配置部署能力);
- 项目正确的部署到客户的平台,并保证运行正常;
- 维护阶段
- 发现bug解决/项目升级。



