# 第十章 网络编程
# 10.1 网络的相关概念
# 10.2 InetAddress类
相关方法:
# 10.3 Socket
# 10.3.1 Socket基本介绍
(1)Socket(套接字)开发网络应用程序被广泛应用
(2)通信的两端都要有Socket,是两台机器间通信的端点
(3)网络通信其实就是Socket间的通信
(4)Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输
# 10.4 TCP网络通信编程
# 10.4.1 TCP字节流编程1
//客户端
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());
//4. 关闭流对象和socket,必须关闭
outputStream.close();
socket.close();
System.out.println("客户端退出");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//服务端
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//1. 在本机的9999端口监听,等待连接
// 细节: 要求在本机没有其他服务在监听9999
ServerSocket serverSocket = new ServerSocket(9999);//ServerSocket可以创建多个Socket
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. 关闭流
inputStream.close();
socket.close();
serverSocket.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 10.4.2 TCP字节流编程2
//客户端
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());
//4. 结束标记
socket.shutdownOutput();
//5. 获取和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));
}
//6. 关闭流对象和socket,必须关闭
outputStream.close();
socket.close();
System.out.println("客户端退出");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//1. 在本机的9999端口监听,等待连接
// 细节: 要求在本机没有其他服务在监听9999
ServerSocket serverSocket = new ServerSocket(9999);//ServerSocket可以创建多个Socket
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());
//6. 设置结束标记
socket.shutdownOutput();
//5. 关闭流
inputStream.close();
socket.close();
serverSocket.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 10.4.3 TCP字符流编程
//客户端
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. 通过输出流,写入数据到数据通道,使用字符流
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write("hello, server 字符流");
bufferedWriter.newLine(); //插入一个换行符,表示写入的内容结束,注意:这样写要要求对方使用readLine()方法读取数据
bufferedWriter.flush();//如果使用的字符流,需要手动刷新,否则数据不会写入数据通道
//5. 获取和socket关联的输入流,读取数据(字节)并显示
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();
System.out.println(s);
//6. 关闭流对象和socket,必须关闭
bufferedReader.close();
bufferedWriter.close();
socket.close();
System.out.println("客户端退出");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//服务端
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//1. 在本机的9999端口监听,等待连接
// 细节: 要求在本机没有其他服务在监听9999
ServerSocket serverSocket = new ServerSocket(9999);//ServerSocket可以创建多个Socket
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读取,使用字符流
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
//5. 关闭流
bufferedWriter.close();
bufferedReader.close();
socket.close();
serverSocket.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 10.4.4 TCP网络通信编程应用实例
# 10.4.5 netstat指令
(1)netstat -an 可以查看当前主机网络情况,包括端口监听情况和网络连接情况
(2)netstat -an | more 可以分页显示
# 10.5 UDP网络通信编程
public class UDPReceiverA {
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);
//可以把packet进行拆包,取出数据并显示
int length = packet.getLength(); //实际接收到的数据字节长度
byte[] data = packet.getData(); //接收到的数据
String s = new String(data, 0, length);
System.out.println(s);
//回复信息给B端
//将需要发送的数据封装到 DatagramPacket对象
data = "好的, 明天见".getBytes();
packet = new DatagramPacket(data, data.length, InetAddress.getByName("10.107.32.30"), 9998);
socket.send(packet);
socket.close();
System.out.println("A端退出");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class UDPSenderB {
public static void main(String[] args) throws IOException {
//创建 DatagramSocket对象,准备在9998端口发送数据
DatagramSocket socket = new DatagramSocket(9998);
//将需要发送的数据封装到DatagramPacket对象
byte[] data = "hello 明天吃火锅".getBytes();
//封装的DatagramPacket对象data内容是:字节数组, data.length, 主机(ip), 端口
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("10.107.32.30"), 9999);
socket.send(packet);
//接收从A端回复的信息
//2. 构建一个 DatagramPacket 对象,准备接收数据
// 在前面讲解UDP协议时,一个数据包最大64k
byte[] buf = new byte[1024];
packet = new DatagramPacket(buf, buf.length);
//3. 调用接收方法,将通过网络传输的DatagramPacket对象填充到packet对象
//当有数据包发送到本机的9998端口时,就会接收到数据,如果没有数据包发送到本机的9998端口,就会阻塞等待
System.out.println("接收端A 等待接收数据");
socket.receive(packet);
//可以把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端退出");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 10.6 TCP文件下载
//文件下载的客户端
public class Homework03Client {
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(), 9999);
//3. 获取和Socket关联的输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write(downloadFileName.getBytes());
//4. 设置结束的标志
socket.shutdownOutput();
//5. 读取服务端返回的文件(字节数据)
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);
//6. 得到一个输出流,准备将 bytes 写入到磁盘文件
String filePath = "d:\\桌面\\" + downloadFileName + ".mp3";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
bos.write(bytes);
//7. 关闭相关的资源
bos.close();
bis.close();
outputStream.close();
socket.close();
System.out.println("客户端下载完毕,正确退出");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//先写文件下载的服务端
public class Homework03Server {
public static void main(String[] args) throws Exception {
//1. 监听 9999 端口
ServerSocket serverSocket = new ServerSocket(9999);
//2. 等待客户端连接
System.out.println("服务器在9999端口监听等待下载文件");
Socket socket = serverSocket.accept();
//3. 读取 客户端发送要下载的文件名
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 ("高山流水".equals(downLoadFileName)) {
resFileName = "src\\高山流水.mp3";
} else {
resFileName = "src\\无名.mp3";
}
//创建一个输入流用来读取文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(resFileName));
//使用工具类StreamUtils,读取文件到一个字节数组
byte[] bytes = StreamUtils.streamToByteArray(bis);
//得到与Socket关联的输出流
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
//写入到数据通道,返回给客户端
bos.write(bytes);
//设置结束标记
socket.shutdownOutput();
//关闭相关的资源
bis.close();
inputStream.close();
socket.close();
serverSocket.close();
System.out.println("服务端退出");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* 此类用于演示关于流的读写方法
*
*/
public class StreamUtils {
/**
* 功能:将输入流转换成byte[], 即可以把文件的内容读入到byte[]
*/
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);//把读取到的数据,写入bos
}
byte[] array = bos.toByteArray();//然后将bos 转成字节数组
bos.close();
return array;
}
/**
* 功能:将InputStream转换成String
*/
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();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33