网络编程
一、网络通信
1、概念
两台设备之间通过网络实现数据传输;
2、网络通信
将数据通过网络从一台设备传输到另一台设备;
Java.net包下提供了一系列的类或接口,供程序员使用,完成网络通信。
二、网络
1、概念
两台或多台设备通过一定物理设备连接起来构成了网络。
2、基于网络的覆盖范围进行分类
- 局域网:覆盖范围最小,仅仅覆盖一个教师或一个机房;
- 城域网:覆盖范围较大,可以覆盖一个城市;
- 广域网:覆盖范围最大,可以覆盖全国,甚至全球,万维网是广域网的代表。
三、IP地址
(1)概念:用于唯一表示网络中的每台计算机/主机;
(2)查看ip地址:ipconfig
;
(3)ip地址的表示形式:点分十进制 xx.xx.xx.xx;(4字节,32位)
(4)每一个十进制数的范围:0-255;
(5)ip地址的组成 = 网络地址 + 主机地址 ,比如:192.128.16.69;
(6)IPv6是互联网工程任务组设计的用于替代IPv4的下一代IP协议,其地址数量号可以称为全世界的每一粒沙子编上一个地址;(128位,16个字节,按照16进制表示X:X:X:X:X:X:X:X)这里X是一个四位16进制数,一个X有16位,2个字节
(7)由于IPv4最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展。IPv6的使用,不仅能解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍。
四、域名
五、端口号
用于区分监听的是哪个服务
1、概念
用于表示计算机上某个特定的网络程序
2、表示形式
以整数形式,端口范围0 ~ 65535【2个字节表示端口0~(2的16次方)-1】
3、常见的网络程序端口号
0~1024
已经被占用,比如ssh 22,ftp 21,smtp 25,http 80;
tomcat
8080,mysql
3306,oracle
1521,sqlserver
1433
【在网络开发中,不要使用0~1024端口】
六、网络通信协议
1、协议(tcp/ip)
TCP/IP(Transmission Control Protocol/Internet Protocol)。中文名为传输控制协议/因特网互联协议,又叫网络通讯协议
这个协议是Internet最基本的协议、Internet国际互联网络的基础,简单来说,就是由网络层的IP协议和传输层的TCP协议组成的。
数据的组织形式就是协议
2、模型
3、示意图
七、TCP和UDP
1、TCP协议(传输控制协议)
① 使用TCP协议前,须先建立TCP连接,形成传输数据通道;
② 传输前,采用“三次握手”方式,确认是可靠的;
③ TCP协议进行通信的两个应用进程:客户端、服务端;
④ 在连接中可进行大数据量的传输;
⑤ 传输完毕,需释放已建立的连接,效率低。
2、UDP协议(用户数据协议)
① 将数据、源、目的封装成数据包,不需要建立连接;
② 每个数据报的大小限制在64K内,不适合传输大量数据;
③ 因无需连接,故是不可靠的;(有可能数据并没有传输过去)
④ 发送数据结束时无需释放资源(因为不是面向连接的),速度快;
⑤ 举例:测试通知:发短信。
八、InetAddress类
1、相关方法
1)getLocalHost
获取本机InetAddress对象
2)getByName
根据指定主机名/域名获取IP地址对象
3)getHostName
获取InetAddress对象的主机名
4)getHostAddress
获取InetAddress对象的地址
2、应用案例
public class API {
public static void main(String[] args) throws UnknownHostException {
//1. getLocalHost(): 获取本机的InetAddress对象,返回【主机名/IP地址】
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("localHost=" + localHost); //RXLI-GAMECOMPUTER/192.168.220.1
//2. getByName(String hostName): 根据主机名返回InetAddress对象,返回【主机名/IP地址】
InetAddress host1 = InetAddress.getByName("RXLI-GAMECOMPUTER");
System.out.println("host1=" + host1); //RXLI-GAMECOMPUTER/192.168.220.1
//3. getByName(String domainName): 根据域名返回InetAddress对象,返回【域名/IP地址】
InetAddress host2 = InetAddress.getByName("www.baidu.com");
System.out.println("host2=" + host2); //www.baidu.com/14.119.104.254(这个ip是服务器ip,可以变化的)
//4. getHostAddress(): 通过InetAddress对象,获取对应的地址,返回【String】
String hostAddress = host2.getHostAddress();
System.out.println("host2对应的ip=" + hostAddress); //host2 对应的ip=14.119.104.189
}
}
九、Socket套接字
1、基本介绍
(1)套接字(Socket)开发网络应用程序被广泛采用,以至于成为事实上的标准;
(2)通讯的两端都要有Socket,是两台机器之间的通讯端点;
(3)网络通信其实就是Socket间的通信;
(4)Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输(理解成客户端和服务端的插头)
(5)一般主动发起通信的应用程序属于客户端,等待通信请求的为服务端。
2、TCP网络通信编程
1)基本介绍
① 基于客户端-服务端的网络通信;
② 底层使用的是TCP/IP协议;
③ 应用场景举例:客户端发送数据,都无端接受并显示控制台;
④ 基于Sockte的TCP编程。
⭐注意:Socket最后一定要关闭!!
2)应用实例1
先运行server,再运行client
虚拟机—Server
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//1. new ServerSocket(int port) 客户端监听
//如果该端口被占用或者端口不存在,会报错
//ServerSocket可以通过accept()方法返回多个Socket
//不同的客户端连接同一个服务器时,对应不同的Socket,可以处理并发
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端9999端口开始监听,等待连接...");
//2. accept() 等待连接
// 如果没有得到连接,则一直阻塞在这,不会执行下面的代码
// 如果得到连接,则返回一个Socket对象,是Server的socket
Socket socket = serverSocket.accept();
System.out.println("服务端得到连接...");
//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(注意还有severSocket)
inputStream.close();
serverSocket.close();
socket.close();
}
}
主机—Client
public class SocketTCP01Client {
public static void main(String[] args) throws IOException {
//1. 设置服务端主机ip
String serverIP = "192.168.220.129";
//2. 连接服务端 new Socket(String ip , int port)
// 如果连接失败,则报错
// 如果连接成功,则返回一个Socket对象,是客户端的socket
Socket socket = new Socket(serverIP, 9999);
System.out.println("客户端连接成功...");
//3. 通过socket.getOutputStream() 获取输出流对象
// 相当于把数据写入到两个socket连接的线路上
OutputStream outputStream = socket.getOutputStream();
//4. 通过IO输出流,写入数据到数据通道
outputStream.write("hello server".getBytes()); //字节流,得将字符串转为字节数组
//5. 关闭流对象和socket
outputStream.close();
socket.close();
}
}
3)应用实例2
写入结束标记:socket.shutdownOutput();
在每次输出流结束后写入结束标记
⭐分析:
如果不写入结束标记,程序会卡在服务端接收客户端传来消息的位置(服务端会一直接收,因为客户端并没有给它一个结束标记)
由于服务端没有接受完,就不会执行下面的写入hello client的语句,客户端也就更不会执行读取hello client的语句
虚拟机—server
public class SocketTCP02Server {
public static void main(String[] args) throws IOException {
//1. new ServerSocket(int port) 客户端监听
//如果该端口被占用或者端口不存在,会报错
//ServerSocket可以通过accept()方法返回多个Socket
//不同的客户端连接同一个服务器时,对应不同的Socket,可以处理并发
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端9999端口开始监听,等待连接...");
//2. accept() 等待连接
// 如果没有得到连接,则一直阻塞在这,不会执行下面的代码
// 如果得到连接,则返回一个Socket对象,是Server的socket
Socket socket = serverSocket.accept();
System.out.println("服务端得到连接...");
//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();
//6. 通过IO输出流输出“hello client"
outputStream.write("hello client".getBytes());
//7. 写入结束标记
socket.shutdownOutput();
//8. 关闭流和socket(注意还有severSocket)
inputStream.close();
outputStream.close();
serverSocket.close();
socket.close();
System.out.println("服务端退出...");
}
}
客户端—client
public class SocketTCP02Client {
public static void main(String[] args) throws IOException {
//1. 设置服务端主机ip
String serverIP = "192.168.220.129";
//2. 连接服务端 new Socket(String ip , int port)
// 如果连接失败,则报错
// 如果连接成功,则返回一个Socket对象,是客户端的socket
Socket socket = new Socket(serverIP, 9999);
System.out.println("客户端连接成功...");
//3. 通过socket.getOutputStream() 获取输出流对象
// 相当于把数据写入到两个socket连接的线路上
OutputStream outputStream = socket.getOutputStream();
//4. 通过IO输出流,写入数据到数据通道
outputStream.write("hello server".getBytes()); //字节流,得将字符串转为字节数组
//5. 写入结束标记
socket.shutdownOutput();
//6. 通过socket.getInputStream() 获取输入流对象
InputStream inputStream = socket.getInputStream();
//7. 通过IO输入流,读取服务端传过来的数据
byte[] buf = new byte[1024];
int readLen = 0;
while((readLen = inputStream.read(buf)) != -1) {
System.out.println(new String(buf,0,readLen));
}
//8. 关闭流对象和socket
outputStream.close();
inputStream.close();
socket.close();
System.out.println("客户端退出...");
}
}
4)应用实例3【使用字符流】
⭐使用转换流 + 字符流需要写入完成后手动刷新bw.flush()
才能写入
核心代码:
//第一种方法:newLine()结束,这种不能用while循环读取
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
//3. 通过字符输出流,写入数据到 数据通道
bw.write("hello, server");
bw.newLine();//⭐ 插入一个换行符,表示写入的内容结束, 注意,要求对方使用readLine()!!!!
bw.flush();// ⭐ 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道
//第二种方法:socket.shutdownOutput():可以用while,但bw.flush得在结束标志前面
OutputStream outputStream = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
bw.write("hello client");
// 刷新写入
bw.flush();
// 设置结束标记
socket.shutdownOutput();
虚拟机—server
public class SocketTCP03Server {
public static void main(String[] args) throws IOException {
//1. new ServerSocket(int port) 客户端监听
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端9999端口开始监听,等待连接...");
//2. accept() 等待连接
Socket socket = serverSocket.accept();
System.out.println("服务端得到连接...");
//3. 通过socket.getInputStream() 获取输入流对象
InputStream inputStream = socket.getInputStream();
//4. 转换成字符流-->再转成处理流
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
//5. 通过IO输入流读取数据(用处理流一行行读取)
System.out.println(br.readLine());
//6. 获取socket相关联的输出流
OutputStream outputStream = socket.getOutputStream();
//7. 转换成字符流--->再转成处理流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
//8. 通过IO输出流输出“hello client"
bw.write("hello client");
//9. 写入结束标记
//socket.shutdownOutput();
bw.newLine();
//10. 手动刷新(⭐重要!)
bw.flush();
//11. 关闭流和socket(注意还有severSocket)(只关闭外层流)
br.close();
bw.close();
serverSocket.close();
socket.close();
System.out.println("服务端退出...");
}
}
主机—client
public class SocketTCP03Client {
public static void main(String[] args) throws IOException {
//1. 设置服务端主机ip
String serverIP = "192.168.220.129";
//2. 连接服务端 new Socket(String ip , int port)
// 如果连接失败,则报错
// 如果连接成功,则返回一个Socket对象,是客户端的socket
Socket socket = new Socket(serverIP, 9999);
System.out.println("客户端连接成功...");
//3. 通过socket.getOutputStream() 获取输出流对象
// 相当于把数据写入到两个socket连接的线路上
OutputStream outputStream = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
//4. 通过IO输出流,写入数据到数据通道
bw.write("hello server"); //字节流,得将字符串转为字节数组
//5. 写入结束标记
//socket.shutdownOutput();
bw.newLine();
//6. 手动刷新(⭐重要!)
bw.flush();
//7. 通过socket.getInputStream() 获取输入流对象
InputStream inputStream = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
//8. 通过IO输入流,读取服务端传过来的数据
String line = "";
line = br.readLine();
System.out.println(line);
//9. 关闭流对象和socket
bw.close();
br.close();
socket.close();
System.out.println("客户端退出...");
}
}
5)应用实例4:上传图片
① 无工具类版(不打包)
注意:图片是二进制文件,需要用字节流
流程分析:
1. 客户端传文件给服务端:
- 【客户端】
- read文件
- socket.output文件
- 【服务端】
- socket.input文件
- write文件
2. 服务端传信息给客户端
- 【服务端】
- socket.output信息
- 【客户端】
- socket.input信息
虚拟机—服务端
【⭐】在java文件中设置linux下的文件路径 --> File.separator
public class TCPFileUploadServer {
public static final String separator = File.separator; //用于区分windows和linuxs的文件路径分隔符
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务端8888端口开始监听,等待连接...");
Socket socket = serverSocket.accept();
System.out.println("服务端连接成功...");
String filePath = separator + "home" + separator + "rxli"
+ separator + "Pictures" + separator + "qie.png";
//1. 读取socket通道上的数据,并存入图片目录,边读边写
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
int len = 0;
byte[] buf = new byte[1024];
while((len = bis.read(buf)) != -1) {
bos.write(buf,0,len);
}
//写的时候一定要flush
bos.flush();
System.out.println("服务端图片存储完成");
//2. 告知客户端已经存储完图片
BufferedOutputStream bos2 = new BufferedOutputStream(socket.getOutputStream()); //前面个流可以弃掉了,这次输出流在socket上
bos2.write("服务端收到图片啦".getBytes());
//写的时候一定要flush
bos2.flush();
//给对方发消息一定要有结束标记
socket.shutdownOutput();
//关闭流和socket
bos.close();
bos2.close();
bis.close();
socket.close();
serverSocket.close();
System.out.println("服务端退出...");
}
}
主机—客户端
public class TCPFileUploadClient {
public static void main(String[] args) throws IOException {
String serverIP = "192.168.220.129";
Socket socket = new Socket(serverIP, 8888);
//1. 读取磁盘上的图片文件并写入socket通道,边读边写
String filePath = "D:\\IDEA_Projects\\Study\\hsp3\\src\\qie.png";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
int len = 0;
byte[] buf = new byte[1024];
while((len = bis.read(buf)) != -1) {
//边读边写
bos.write(buf,0,len);
}
//输出流在中间一定要flush才能写入
bos.flush();
//写完记得加结束标记
socket.shutdownOutput();
System.out.println("客户端传图片完成");
//2. 接收服务端传来的信息
BufferedInputStream bis2 = new BufferedInputStream(socket.getInputStream());
while((len = bis2.read(buf)) != -1) {
System.out.println(new String(buf,0,len));
}
//关闭流
bos.close();
bis.close();
bis2.close();
socket.close();
System.out.println("客户端退出...");
}
}
传过去的小企鹅:
② 有工具类版(IDEA打包,Linux运行jar包)
StreamUtils工具类
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
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) {
builder.append(line + "\r\n");
}
return builder.toString();
}
}
client类
public class TCPFileUploadClient {
public static void main(String[] args) throws IOException {
String serverIP = "192.168.220.129";
Socket socket = new Socket(serverIP, 8888);
//1. 读取磁盘上的图片文件
String filePath = "D:\\IDEA_Projects\\Study\\hsp3\\src\\qie.png";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
//使用自定义工具类,直接得到输入流上的文件字节数组
byte[] bytes = StreamUtils.streamToByteArray(bis);
//2. 将文件字节数组上传到socket.output通道
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(bytes);
//输出流在中间一定要flush或者close才能写入
bos.flush();
//写完记得加结束标记
socket.shutdownOutput();
System.out.println("客户端传图片完成");
//2. 接收服务端传来的信息
BufferedInputStream bis2 = new BufferedInputStream(socket.getInputStream());
//使用自定义工具类,直接得到输入流上的文件信息(String)
String s = StreamUtils.streamToString(bis2);
System.out.print("服务端发来消息:" + s);
//关闭流
bos.close();
bis.close();
bis2.close();
socket.close();
System.out.println("客户端退出...");
}
}
server类
public class TCPFileUploadServer {
public static final String separator = File.separator; //用于区分windows和linuxs的文件路径分隔符
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务端8888端口开始监听,等待连接...");
Socket socket = serverSocket.accept();
System.out.println("服务端连接成功...");
String filePath = separator + "home" + separator + "rxli"
+ separator + "Pictures" + separator + "qie.png";
//1. 读取socket通道上的数据
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
//转换成字节数组
byte[] bytes = StreamUtils.streamToByteArray(bis);
//2. 存入图片
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
bos.write(bytes);
//写的时候一定要flush
bos.flush();
System.out.println("服务端图片存储完成");
//3. 告知客户端已经存储完图片
BufferedOutputStream bos2 = new BufferedOutputStream(socket.getOutputStream());
bos2.write("我收到图片啦".getBytes());
//写的时候一定要flush
bos2.flush();
//给对方发消息一定要有结束标记
socket.shutdownOutput();
//关闭流和socket
bos.close();
bos2.close();
bis.close();
socket.close();
serverSocket.close();
System.out.println("服务端退出...");
}
}
6)netstat指令
① netstat -an
可以查看当前主机网络情况,包括端口监听情况和网络连接情况;
② netstat -an | more
可以分页显示;
③ netstat -anb
可以查看是哪个程序在使用端口
③ 要求在dos控制台下执行win + r(cmd或者powershell)
虚拟机:(Linux)
netstat -l
:显示监听的套接口
netstat -a
:查看所有网络状态
运行客户端程序后(把结束标志注释掉,故意让连接一直存在)
可以看到主机53722端口连上了虚拟机8888端口
【⭐】客户端的端口是系统自动随机分配的,当传输完成就释放掉
【客户端的端口可变,服务端端口固定】
说明:
Listening 表示某个端口在监听;
如果由一个外部程序(客户端)连接到该端口,就会显示一条连接信息;
可以输入 ctrl + c 退出指令。
查看是哪个程序在用端口
netstat -anb
可以看到是java.exe在用这个端口(主机53722)
3、UDP网络通信编程
1)基本介绍
2)基本流程
DatagramPacket(byte[], int, int, InetAddress ip, int port)
//发送
DatagramPacket(byte[])
//接收
3)示意图
4)应用案例
public class A {
public static void main(String[] args) throws IOException {
//1. 创建一个 DatagramSocket 对象,准备在9999接收数据
DatagramSocket socket = new DatagramSocket(9999);
//2. 构建一个 DatagramPacket 对象,准备接收数据
// 在前面讲解UDP 协议时,老师说过一个数据包最大 64k
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
//3. 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
// 填充到 packet对象
//老师提示: 当有数据包发送到 本机的9999端口时,就会接收到数据
// 如果没有数据包发送到 本机的9999端口, 就会阻塞等待.
System.out.println("接收端A 等待接收数据..");
socket.receive(packet);
//4. 可以把packet 进行拆包,取出数据,并显示.
int length = packet.getLength();//实际接收到的数据字节长度
byte[] data = packet.getData();//接收到数据
String s = new String(data, 0, length);
System.out.println(s);
//===回复信息给B端
//将需要发送的数据,封装到 DatagramPacket对象
data = "好的, 明天见".getBytes();
//说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
packet =
new DatagramPacket(data, data.length, InetAddress.getByName("192.168.220.1"), 9099);
socket.send(packet);//发送
//5. 关闭资源
socket.close();
System.out.println("A端退出...");
}
}
/**
* 发送端B ====> 也可以接收数据
*/
@SuppressWarnings({"all"})
public class B {
public static void main(String[] args) throws IOException {
//1.创建 DatagramSocket 对象,准备在9099端口 接收数据
DatagramSocket socket = new DatagramSocket(9099);
//2. 将需要发送的数据,封装到 DatagramPacket对象
byte[] data = "hello 明天吃火锅~".getBytes(); //
//说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
DatagramPacket packet =
new DatagramPacket(data, data.length, InetAddress.getByName("192.168.220.129"), 9999);
socket.send(packet);
//3.=== 接收从A端回复的信息
//(1) 构建一个 DatagramPacket 对象,准备接收数据
// UDP 协议一个数据包最大 64kB
byte[] buf = new byte[1024];
packet = new DatagramPacket(buf, buf.length);
//(2) 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
// 填充到 packet对象
//老师提示: 当有数据包发送到 本机的9998端口时,就会接收到数据,存放在packet中
// 如果没有数据包发送到 本机的9998端口, 就会阻塞等待.
socket.receive(packet);
//(3) 可以把packet 进行拆包,取出数据,并显示.
int length = packet.getLength();//实际接收到的数据字节长度
data = packet.getData();//接收到数据
String s = new String(data, 0, length);
System.out.println(s);
//关闭资源
socket.close();
System.out.println("B端退出");
}
}
4、课后作业
1)编程题
Server
public class Homework01Server {
public static void main(String[] args) throws IOException {
//获取服务端主机名
String name = InetAddress.getLocalHost().getHostName();
//接收客户端发来的消息
//1. serverSocket建立连接
ServerSocket serverSocket = new ServerSocket(9111);
System.out.println("server正在等待连接...");
Socket socket = serverSocket.accept();
System.out.println("连接成功");
//2. socket获取数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
switch (br.readLine()) {
case "name":
bw.write("我是 " + name);
break;
case "hobby":
bw.write("编写java程序");
break;
default:
bw.write("你说啥呢");
break;
}
//先刷新再socket结束
bw.flush();
socket.shutdownOutput();
//关闭流
br.close();
bw.close();
socket.close();
System.out.println("server退出...");
}
}
Client
public class Homework02Client {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
//和虚拟机建立连接
Socket socket = new Socket("192.168.220.129", 9111);
//键盘接收用户问题
System.out.print("请输入你的问题(name-问名字 , hobby-问爱好): ");
String question = scanner.next();
//传数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(question);
bw.flush();
socket.shutdownOutput();
//接收数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(br.readLine());
//关闭资源
bw.close();
br.close();
socket.close();
System.out.println("client退出...");
}
}
2)编程题
serverA端
public class Homework02ReceiverA {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(8888);
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
System.out.println("B端等待A端传入数据...");
datagramSocket.receive(packet);
System.out.println("连接成功...");
int len = packet.getLength();
byte[] b = packet.getData();
String s = new String(b,0,len);
String b_ip = "192.168.220.1";
String ans = "";
if(s.equals("四大名著是哪些")){
ans = "四大名著是《红楼梦》《西游记》《三国演义》《水浒传》";
} else {
ans = "what?";
}
b = ans.getBytes();
packet = new DatagramPacket(b, 0 , b.length,
InetAddress.getByName(b_ip),9888);
datagramSocket.send(packet);
datagramSocket.close();
System.out.println("A端退出...");
}
}
ReceiverB端
public class Homework02SenderB {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
DatagramSocket datagramSocket = new DatagramSocket(9888);
String a_ip = "192.168.220.129";
//键盘输入问题
System.out.print("请输入你的问题:");
byte[] question = scanner.next().getBytes();
DatagramPacket packet = new DatagramPacket(question,0,question.length,
InetAddress.getByName(a_ip),8888);
datagramSocket.send(packet);
byte[] bytes = new byte[1024];
packet = new DatagramPacket(bytes,bytes.length);
datagramSocket.receive(packet);
int len = packet.getLength();
byte[] b = packet.getData();
System.out.println(new String(b,0,len));
datagramSocket.close();
System.out.println("B端退出...");
}
}
3)编程题
StreamUtils
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
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) {
builder.append(line + "\r\n");
}
return builder.toString();
}
}
Server
public class Server {
public static void main(String[] args) throws Exception {
//1 监听 9999端口
ServerSocket serverSocket = new ServerSocket(8555);
//2.等待客户端连接
System.out.println("服务端,在8555端口监听,等待下载文件");
Socket socket = serverSocket.accept();
//3.读取 客户端发送要下载的文件名
// 这里老师使用了while读取文件名,时考虑将来客户端发送的数据较大的情况
InputStream inputStream = socket.getInputStream();
byte[] b = new byte[1024];
int len = 0;
String downLoadFileName = "";
while ((len = inputStream.read(b)) != -1) {
downLoadFileName += new String(b, 0 , len);
}
System.out.println("客户端希望下载文件名=" + downLoadFileName);
//老师在服务器上有两个文件, 无名.mp3 高山流水.mp3
//如果客户下载的是 高山流水 我们就返回该文件,否则一律返回 无名.mp3
String resFileName = "";
if("fire".equals(downLoadFileName)) {
resFileName = "src\\fire.mp3";
} else {
resFileName = "src\\false.mp3";
}
System.out.println("取出的文件filePath=" + resFileName);
//4. 创建一个输入流,读取文件
BufferedInputStream bis =
new BufferedInputStream(new FileInputStream(resFileName));
//5. 使用工具类StreamUtils ,读取文件到一个字节数组
byte[] bytes = StreamUtils.streamToByteArray(bis);
//6. 得到Socket关联的输出流
BufferedOutputStream bos =
new BufferedOutputStream(socket.getOutputStream());
//7. 写入到数据通道,返回给客户端
bos.write(bytes);
bos.flush();
socket.shutdownOutput();//很关键.
//8 关闭相关的资源
bis.close();
inputStream.close();
socket.close();
serverSocket.close();
System.out.println("服务端退出...");
}
}
Client
public class Client {
public static void main(String[] args) throws Exception {
//1. 接收用户输入,指定下载文件名
Scanner scanner = new Scanner(System.in);
System.out.println("请输入下载文件名");
String downloadFileName = scanner.next();
//2. 客户端连接服务端,准备发送
Socket socket = new Socket(InetAddress.getLocalHost(), 8555);
//3. 获取和Socket关联的输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write(downloadFileName.getBytes());
//设置写入结束的标志
socket.shutdownOutput();
//4. 读取服务端返回的文件(字节数据)
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);
//5. 得到一个输出流,准备将 bytes 写入到磁盘文件
//比如你下载的是 高山流水 => 下载的就是 高山流水.mp3
// 你下载的是 韩顺平 => 下载的就是 无名.mp3 文件名 韩顺平.mp3
String filePath = "d:\\" + downloadFileName + ".mp3";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
bos.write(bytes);
//6. 关闭相关的资源
bos.close();
bis.close();
outputStream.close();
socket.close();
System.out.println("客户端下载完毕,正确退出..");
}
}