=============================================================================
=============================================================================
涉及到的知识点有:
1:网络编程(理解)
(1)网络编程的概述
(2)网络参考模型
(3)网络通信的三要素
A:IP地址
B:端口
C:通信协议
(4)Socket机制
(5)UDP协议发送和接收数据(掌握)
(6)TCP协议发送和接收数据(掌握)
(7)案例:
A:UDP
a:最基本的UDP协议发送和接收数据
b:把发送数据改进为键盘录入
c:一个简易聊天小程序并用多线程改进
B:TCP
a:最基本的TCP协议发送和接收数据
b:服务器给出反馈
c:客户端键盘录入,服务器输出控制台(字符流)
d:客户端键盘录入,服务器写到文本文件(字符流)
e:客户端读取文本文件,服务器写到文本文件/输出控制台(字符流)
f:上传图片(字节流)
注意:在通道字节流中要使用flush()刷新方法。否则数据会有丢失。
g:多线程改进上传文件
=============================================================================
=============================================================================
1:网络编程(理解)
(1)网络编程的概述
网络编程:用Java语言实现计算机间数据的信息传递和资源共享。
(2)网络参考模型
OSI参考模型(Open System Interconnection:开放系统互连)
TCP/IP参考模型
--------------------------------------
(3)网络通信的三要素
A:IP地址
a:点分十进制
b:IP地址的组成
c:IP地址的分类
d:两个DOS命令
e:InetAddress类(为了方便我们对IP地址的获取和操作,java提供了一个类InetAddress供我们使用)
InetAddress类的成员方法:(注意:该类中没有构造方法)
public static InetAddress getByName(String host) 根据主机名或者IP地址的字符串表示得到IP地址对象
B:端口
每个程序都会至少有一个逻辑端口。
是应用程序的标识。
范围:0-65535。其中0-1024不建议使用。
C:通信协议
UDP:数据打包,有限制,不连接,效率高,不可靠。
TCP:建立数据通道,无限制,效率低,可靠。
--------------------------------------
(4)Socket机制
A:通信两端都有Socket对象。
B:所有的通信都是通过Socket间的IO进行操作的。
C:网络通信其实就是Socket间的通信。
--------------------------------------
(5)UDP协议发送和接收数据(掌握)
发送:
A:创建发送端Socket对象(DatagramSocket)
B:创建数据,并把数据打包(DatagramPacket)
C:调用Socket对象的发送方法发送数据报包
D:释放资源
接收:
A:创建接收端Socket对象,并指定端口号(DatagramSocket)
B:创建一个数据包(接收容器)(DatagramPacket)
C:调用Socket对象的接收方法接收数据
D:解析数据包,并显示在控制台
E:释放资源
Exception in thread "main" java.net.BindException: Address already in use: Cannot bind
多次启动接收端出现异常。端口被占用。
--------------------------------------
(6)TCP协议发送和接收数据(掌握)
发送:
A:创建发送端Socket对象,并明确要连接的服务器(Socket)
A步骤如果创建对象成功,就说明连接通道已建立成功了。
B:调用Socket对象获取输出流对象,写数据(OutputStream)
C:释放资源
接收:
A:创建接收端Socket对象,并指定端口(ServerSocket)
B:监听客户端连接,返回一个对应的Socket对象
C:获取输入流对象,读取数据显示在控制台(InputStream)
D:释放资源
Exception in thread "main" java.net.ConnectException: Connection refused: connect
连接被拒绝。TCP协议一定要先开服务器。
因为TCP保证数据一定被收到,所以接收端一定要先开启。
--------------------------------------
(7)案例:
A:UDP
a:最基本的UDP协议发送和接收数据
基本版本:


1 package cn.itcast_02;
2
3 import java.io.IOException;
4 import java.net.DatagramPacket;
5 import java.net.DatagramSocket;
6 import java.net.InetAddress;
7 /*
8 * UDP协议发送数据:
9 * A:创建发送端Socket对象(DatagramSocket)
10 * B:创建数据,并把数据打包(DatagramPacket)
11 * C:调用Socket对象的发送方法发送数据报包
12 * D:释放资源
13 *
14 * DatagramSocket类:数据报套接字类,此类表示用来发送和接收数据报包的套接字。
15 */
16 public class SendDemo {
17 public static void main(String[] args) throws IOException {
18 // 创建发送端Socket对象(DatagramSocket)
19 // DatagramSocket类的构造方法:public DatagramSocket()
20 DatagramSocket ds = new DatagramSocket();
21
22 // 创建数据,并把数据打包(DatagramPacket)
23 // DatagramPacket类的构造方法:public DatagramPacket(byte[] buf, int length, InetAddress address, int port)
24 // 创建数据
25 byte[] bys = "hello,UDP,我来了".getBytes(); // 把字符串转为字符数组
26 // 长度
27 int length = bys.length;
28 // 获取IP地址对象
29 InetAddress address = InetAddress.getByName("192.168.40.9");
30 // 端口
31 int port = 10086;
32 DatagramPacket dp = new DatagramPacket(bys, length, address, port);
33
34 // 调用Socket对象的发送方法发送数据报包
35 // DatagramSocket类的成员方法:public void send(DatagramPacket p) 发送数据报包
36 ds.send(dp);
37
38 // 释放资源
39 ds.close();
40 }
41 }
SendDemo.java


1 package cn.itcast_02;
2
3 import java.io.IOException;
4 import java.net.DatagramPacket;
5 import java.net.DatagramSocket;
6 import java.net.InetAddress;
7
8 /*
9 * UDP协议接收数据:
10 * A:创建接收端Socket对象,并指定端口号(DatagramSocket)
11 * B:创建一个数据包(接收容器)(DatagramPacket)
12 * C:调用Socket对象的接收方法接收数据
13 * D:解析数据包,并显示在控制台
14 * E:释放资源
15 */
16 public class ReceiveDemo {
17 public static void main(String[] args) throws IOException {
18 // 创建接收端Socket对象,并指定端口号(DatagramSocket)
19 // DatagramSocket类的构造方法:public DatagramSocket(int port)
20 DatagramSocket ds = new DatagramSocket(10086);
21
22 // 创建一个数据包(接收容器)(DatagramPacket)
23 // DatagramPacket类的构造方法:public DatagramPacket(byte[] buf, int length)
24 byte[] bys = new byte[1024];
25 int length = bys.length;
26 DatagramPacket dp = new DatagramPacket(bys, length);
27
28 // 调用Socket对象的接收方法接收数据
29 // DatagramSocket类的成员方法:public void receive(DatagramPacket p) 接收数据报包,当此方法返回时,DatagramPacket的缓冲区里填充了接收的数据,数据报包也包含发送方的IP地址和发送方机器上的端口号。
30 ds.receive(dp); // 阻塞式(此方法在接收到数据报前一直阻塞)
31
32 // 解析数据包,并显示在控制台
33 // DatagramPacket类的成员方法:public InetAddress getAddress() // 获取IP地址(此时为发送端的IP地址)
34 InetAddress address = dp.getAddress();
35 String ip = address.getHostAddress();
36 // DatagramPacket类的成员方法:public byte[] getData() 获取数据缓冲区
37 // DatagramPacket类的成员方法:public int getLength() 获取数据的实际长度
38 byte[] bys2 = dp.getData();
39 int len = dp.getLength();
40 String s = new String(bys2, 0, len);
41 System.out.println(ip + "传递的数据是:" + s);
42
43 // 释放资源
44 ds.close();
45 }
46 }
ReceiveDemo.java
改进版本:(使用链式编程)


1 package cn.itcast_03;
2
3 import java.io.IOException;
4 import java.net.DatagramPacket;
5 import java.net.DatagramSocket;
6 import java.net.InetAddress;
7 /*
8 * UDP协议发送数据:
9 * A:创建发送端Socket对象(DatagramSocket)
10 * B:创建数据,并把数据打包(DatagramPacket)
11 * C:调用Socket对象的发送方法发送数据报包
12 * D:释放资源
13 *
14 * DatagramSocket类:数据报套接字类,此类表示用来发送和接收数据报包的套接字。
15 */
16 public class SendDemo {
17 public static void main(String[] args) throws IOException {
18 // 创建发送端Socket对象(DatagramSocket)
19 DatagramSocket ds = new DatagramSocket();
20
21 // 创建数据,并把数据打包(DatagramPacket)
22 byte[] bys = "hello,UDP,我来了".getBytes(); // 把字符串转为字符数组
23 DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.40.9"), 10086);
24
25 // 调用Socket对象的发送方法发送数据报包
26 ds.send(dp);
27
28 // 释放资源
29 ds.close();
30 }
31 }
SendDemo.java


1 package cn.itcast_03;
2
3 import java.io.IOException;
4 import java.net.DatagramPacket;
5 import java.net.DatagramSocket;
6
7 /*
8 * UDP协议接收数据:
9 * A:创建接收端Socket对象,并指定端口号(DatagramSocket)
10 * B:创建一个数据包(接收容器)(DatagramPacket)
11 * C:调用Socket对象的接收方法接收数据
12 * D:解析数据包,并显示在控制台
13 * E:释放资源
14 *
15 * Exception in thread "main" java.net.BindException: Address already in use: Cannot bind
16 * 多次启动接收端出现异常。端口被占用。
17 */
18 public class ReceiveDemo {
19 public static void main(String[] args) throws IOException {
20 // 创建接收端Socket对象,并指定端口号(DatagramSocket)
21 DatagramSocket ds = new DatagramSocket(10086);
22
23 // 创建一个数据包(接收容器)(DatagramPacket)
24 byte[] bys = new byte[1024];
25 DatagramPacket dp = new DatagramPacket(bys, bys.length);
26
27 // 调用Socket对象的接收方法接收数据
28 ds.receive(dp); // 阻塞式(此方法在接收到数据报前一直阻塞)
29
30 // 解析数据包,并显示在控制台
31 String ip = dp.getAddress().getHostAddress();
32 String s = new String(dp.getData(), 0, dp.getLength());
33 System.out.println(ip + "传递的数据是:" + s);
34
35 // 释放资源
36 ds.close();
37 }
38 }
ReceiveDemo.java
b:把发送数据改进为键盘录入


1 package cn.itcast_04;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.net.DatagramPacket;
7 import java.net.DatagramSocket;
8 import java.net.InetAddress;
9 /*
10 * UDP案例:
11 * 从键盘录入数据进行发送,如果输入的是886那么客户端就结束输入数据。
12 *
13 * 数据来自于键盘录入,键盘录入数据要自己控制录入结束。
14 */
15 public class SendDemo {
16 public static void main(String[] args) throws IOException {
17 // 创建发送端Socket对象
18 DatagramSocket ds = new DatagramSocket();
19
20 // 封装键盘录入数据对象
21 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
22 String line = null;
23 while ((line = br.readLine()) != null) {
24 if ("886".equals(line)) {
25 break;
26 }
27
28 // 创建数据,并把数据打包
29 byte[] bys = line.getBytes();
30 // DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.40.9"), 10086);
31 DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.40.255"), 10086); // 广播地址
32
33 // 调用Socket对象的发送方法发送数据报包
34 ds.send(dp);
35 }
36
37 // 释放资源
38 ds.close();
39 }
40 }
SendDemo.java


1 package cn.itcast_04;
2
3 import java.io.IOException;
4 import java.net.DatagramPacket;
5 import java.net.DatagramSocket;
6
7 /*
8 * UDP案例:
9 * 从键盘录入数据进行发送,如果输入的是886那么客户端就结束输入数据。
10 */
11 public class ReceiveDemo {
12 public static void main(String[] args) throws IOException {
13 // 创建接收端Socket对象,并指定端口号
14 DatagramSocket ds = new DatagramSocket(10086);
15
16 while (true) {
17 // 创建一个数据包(接收容器)
18 byte[] bys = new byte[1024];
19 DatagramPacket dp = new DatagramPacket(bys, bys.length);
20
21 // 调用Socket对象的接收方法接收数据
22 ds.receive(dp); // 阻塞式(此方法在接收到数据报前一直阻塞)
23
24 // 解析数据包,并显示在控制台
25 String ip = dp.getAddress().getHostAddress();
26 String s = new String(dp.getData(), 0, dp.getLength());
27 System.out.println(ip + "传递的数据是:" + s);
28 }
29
30 // 释放资源
31 // ds.close(); // 接收端应该一直开着等待接收数据,是不需要关闭的。
32 }
33 }
ReceiveDemo.java
c:一个简易聊天小程序并用多线程改进


1 package cn.itcast_05;
2
3 import java.io.IOException;
4 import java.net.DatagramSocket;
5
6 /*
7 * 通过多线程改进刚才的聊天程序,这样我就可以实现在一个窗口发送和接收数据了
8 */
9 public class ChatRoom {
10 public static void main(String[] args) throws IOException {
11 DatagramSocket dsSend = new DatagramSocket();
12 DatagramSocket dsReceive = new DatagramSocket(10086);
13
14 SendThread st = new SendThread(dsSend);
15 ReceiveThread rt = new ReceiveThread(dsReceive);
16
17 Thread t1 = new Thread(st);
18 Thread t2 = new Thread(rt);
19
20 t1.start();
21 t2.start();
22 }
23 }
ChatRoom.java


1 package cn.itcast_05;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStreamReader;
6 import java.net.DatagramPacket;
7 import java.net.DatagramSocket;
8 import java.net.InetAddress;
9
10 public class SendThread implements Runnable {
11
12 private DatagramSocket ds;
13
14 public SendThread(DatagramSocket ds) {
15 this.ds = ds;
16 }
17
18 @Override
19 public void run() {
20 try {
21 // 封装键盘录入数据对象
22 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
23 String line = null;
24 while ((line = br.readLine()) != null) {
25 if ("886".equals(line)) {
26 break;
27 }
28
29 // 创建数据,并把数据打包
30 byte[] bys = line.getBytes();
31 DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("192.168.40.9"), 10086);
32
33 // 调用Socket对象的发送方法发送数据报包
34 ds.send(dp);
35 }
36 // 释放资源
37 ds.close();
38 } catch (IOException e) {
39 e.printStackTrace();
40 }
41 }
42
43 }
SendThread.java


1 package cn.itcast_05;
2
3 import java.io.IOException;
4 import java.net.DatagramPacket;
5 import java.net.DatagramSocket;
6
7 public class ReceiveThread implements Runnable {
8
9 private DatagramSocket ds;
10
11 public ReceiveThread(DatagramSocket ds) {
12 this.ds = ds;
13 }
14
15 @Override
16 public void run() {
17 try {
18 while (true) {
19 // 创建一个数据包(接收容器)
20 byte[] bys = new byte[1024];
21 DatagramPacket dp = new DatagramPacket(bys, bys.length);
22
23 // 调用Socket对象的接收方法接收数据
24 ds.receive(dp);
25
26 // 解析数据包,并显示在控制台
27 String ip = dp.getAddress().getHostAddress();
28 String s = new String(dp.getData(), 0, dp.getLength());
29 System.out.println("from " + ip + " data is : " + s);
30 }
31 } catch (IOException e) {
32 e.printStackTrace();
33 }
34 }
35
36 }
ReceiveThread.java
B:TCP
a:最基本的TCP协议发送和接收数据
b:服务器给出反馈
c:客户端键盘录入,服务器输出控制台(字符流)
d:客户端键盘录入,服务器写到文本文件(字符流)


1 package cn.itcast_09;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.IOException;
6 import java.io.InputStreamReader;
7 import java.io.OutputStreamWriter;
8 import java.net.Socket;
9
10 /*
11 * 客户端键盘录入,服务器写到文本文件
12 */
13 public class ClientDemo {
14 public static void main(String[] args) throws IOException {
15 // 创建客户端Socket对象
16 Socket s = new Socket("192.168.40.9", 2222);
17
18 // 包装键盘录入数据对象
19 BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
20
21 // 包装通道内的流对象
22 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
23
24 String line = null;
25 while ((line = br.readLine()) != null) {
26 // 键盘录入数据要自定义结束标记
27 if ("886".equals(line)) {
28 break;
29 }
30 bw.write(line);
31 bw.newLine();
32 bw.flush();
33 }
34
35 // 释放资源
36 // bw.close(); // 已经不录入了,关不关流无所谓了
37 // br.close(); // 本质关闭的是s对象
38 s.close();
39 }
40 }
ClientDemo.java


1 package cn.itcast_09;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.FileWriter;
6 import java.io.IOException;
7 import java.io.InputStreamReader;
8 import java.net.ServerSocket;
9 import java.net.Socket;
10 /*
11 * 客户端键盘录入,服务器写到文本文件
12 */
13 public class ServerDemo {
14 public static void main(String[] args) throws IOException {
15 // 创建服务器Socket对象
16 ServerSocket ss = new ServerSocket(2222);
17
18 // 监听客户端连接
19 Socket s = ss.accept();
20
21 // 包装通道内的流对象
22 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
23
24 // 封装文本文件对象
25 BufferedWriter bw = new BufferedWriter(new FileWriter("src//cn//itcast_09//b.txt"));
26
27 String line = null;
28 while ((line = br.readLine()) != null) {
29 bw.write(line);
30 bw.newLine();
31 bw.flush();
32 }
33
34 s.close();
35 bw.close();
36 }
37 }
ServerDemo.java
e:客户端读取文本文件,服务器写到文本文件/输出控制台(字符流)


1 package cn.itcast_10;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.FileReader;
6 import java.io.IOException;
7 import java.io.OutputStreamWriter;
8 import java.net.Socket;
9
10 /*
11 * 客户端读取文本文件,服务器写到文本文件/输出到控制台
12 */
13 public class ClientDemo {
14 public static void main(String[] args) throws IOException {
15 // 创建客户端Socket对象
16 Socket s = new Socket("192.168.40.9", 2222);
17
18 // 封装文本文件对象
19 BufferedReader br = new BufferedReader(new FileReader("src//cn//itcast_10//a.txt"));
20
21 // 包装通道内的流对象
22 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
23
24 String line = null;
25 while ((line = br.readLine()) != null) {
26 bw.write(line);
27 bw.newLine();
28 bw.flush();
29 }
30
31 br.close();
32 s.close();
33 }
34 }
ClientDemo.java


1 package cn.itcast_10;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.FileWriter;
6 import java.io.IOException;
7 import java.io.InputStreamReader;
8 import java.net.ServerSocket;
9 import java.net.Socket;
10 /*
11 * 客户端读取文本文件,服务器写到文本文件/输出到控制台
12 */
13 public class ServerDemo {
14 public static void main(String[] args) throws IOException {
15 // 创建服务器Socket对象
16 ServerSocket ss = new ServerSocket(2222);
17
18 // 监听客户端连接
19 Socket s = ss.accept();
20
21 // 包装通道内的流对象
22 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
23
24 // 封装文本文件对象
25 BufferedWriter bw = new BufferedWriter(new FileWriter("src//cn//itcast_10//Copy.txt"));
26
27 String line = null;
28 while ((line = br.readLine()) != null) {
29 bw.write(line);
30 bw.newLine();
31 bw.flush();
32
33 System.out.println(line);
34 }
35
36 s.close();
37 bw.close();
38 }
39 }
ServerDemo.java
服务器给出反馈的代码:


1 package cn.itcast_12;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.FileReader;
6 import java.io.IOException;
7 import java.io.InputStreamReader;
8 import java.io.OutputStreamWriter;
9 import java.net.Socket;
10
11 /*
12 * 按照我们正常的思路加入反馈信息,结果却没反应。为什么呢?
13 * 读取文本文件是以null作为结束信息的,但是呢,在通道里读数据,通道内并不是这样结束信息的。
14 * 所以,服务器根本就不知道你结束了。而客户端还想服务器给反馈。所以,就相互等待了。
15 *
16 * 如何解决呢?
17 * A:客户端写完数据后,再写一条数据作为结束标记,服务器读取到这条数据说明客户端结束了,所以服务器该结束了。
18 * 这样做可以解决问题,但是不好。
19 * 如果自定义的结束标记与文件的内容相同的话,文件发送就提前结束了。
20 * B:Socket对象提供了一种解决方案
21 * public void shutdownOutput()
22 */
23
24 public class UploadClient {
25 public static void main(String[] args) throws IOException {
26 // 创建客户端Socket对象
27 Socket s = new Socket("192.168.40.9", 11111);
28
29 // 封装文本文件对象
30 BufferedReader br = new BufferedReader(new FileReader("src//cn//itcast_12//a.txt"));
31
32 // 封装通道内流对象
33 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
34
35 String line = null;
36 while ((line = br.readLine()) != null) { // 阻塞
37 // 在文件里读数据,文件末尾是null!
38 bw.write(line);
39 bw.newLine();
40 bw.flush();
41 }
42
43 // 自定义一个结束标记
44 // bw.write("over");
45 // bw.newLine();
46 // bw.flush();
47
48 // Socket类提供了一个终止方法,该方法会通知服务器你别等了,我没有数据过来了
49 s.shutdownOutput();
50
51 // 客户端接收服务器反馈
52 BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream()));
53 String client = brClient.readLine(); // 阻塞
54 System.out.println(client);
55
56 // 释放资源
57 br.close();
58 s.close();
59 }
60 }
UploadClient.java


1 package cn.itcast_12;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.FileWriter;
6 import java.io.IOException;
7 import java.io.InputStreamReader;
8 import java.io.OutputStreamWriter;
9 import java.net.ServerSocket;
10 import java.net.Socket;
11
12 public class UploadServer {
13 public static void main(String[] args) throws IOException {
14 // 创建服务器端Socket对象
15 ServerSocket ss = new ServerSocket(11111);
16
17 // 监听客户端连接
18 Socket s = ss.accept(); // 阻塞
19
20 // 封装通道内的流对象
21 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
22
23 // 封装文本文件对象
24 BufferedWriter bw = new BufferedWriter(new FileWriter("src//cn//itcast_12//Copy.txt"));
25
26 String line = null;
27 while ((line = br.readLine()) != null) { // 阻塞
28 // 在通道里读数据,通道里可没有null!肿么办?
29 // 判断自定义结束标志
30 // if ("over".equals(line)) {
31 // break;
32 // }
33 bw.write(line);
34 bw.newLine();
35 bw.flush();
36 }
37
38 // 服务器给客户端反馈
39 BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
40 bwServer.write("文件上传成功");
41 bwServer.newLine();
42 bwServer.flush();
43
44 // 释放资源
45 bw.close();
46 s.close();
47 }
48 }
UploadServer.java
f:上传图片(字节流)
注意:在通道字节流中要使用flush()刷新方法。否则数据会有丢失。


1 package cn.itcast_13;
2
3 import java.io.BufferedInputStream;
4 import java.io.BufferedOutputStream;
5 import java.io.FileInputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.net.Socket;
9
10 /*
11 * 上传图片(字节流)
12 *
13 * OutputStream抽象类的方法:
14 * public void flush() throws IOException
15 * 刷新此输出流并强制写出所有缓冲的输出字节
16 *
17 * 注意:在通道字节流中要使用flush()刷新方法。否则数据会有丢失。
18 */
19 public class UploadClient {
20 public static void main(String[] args) throws IOException {
21 // 创建客户端Socket对象
22 Socket s = new Socket("192.168.40.9", 19191);
23
24 // 封装图片文件对象(字节流)
25 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src//cn//itcast_13//林青霞.jpg"));
26
27 // 封装通道内的流对象(字节流)
28 BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
29
30 byte[] bys = new byte[1024];
31 int len = 0;
32 while ((len = bis.read(bys)) != -1) {
33 bos.write(bys, 0, len);
34 bos.flush(); // 刷新此输出流并强制写出所有缓冲的输出字节
35 }
36 s.shutdownOutput();
37
38 // 读取服务器的反馈
39 InputStream is = s.getInputStream();
40 byte[] bys2 = new byte[1024];
41 int len2 = is.read(bys2);
42 String client = new String(bys2, 0, len2);
43 System.out.println(client);
44
45 // 释放资源
46 bis.close();
47 s.close();
48 }
49 }
UploadClient.java


1 package cn.itcast_13;
2
3 import java.io.BufferedInputStream;
4 import java.io.BufferedOutputStream;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.OutputStream;
8 import java.net.ServerSocket;
9 import java.net.Socket;
10
11 /*
12 * 上传图片(字节流)
13 */
14 public class UploadServer {
15 public static void main(String[] args) throws IOException {
16 // 创建服务器Socket对象
17 ServerSocket ss = new ServerSocket(19191);
18
19 // 监听客户端连接
20 Socket s = ss.accept();
21
22 // 封装通道内流对象(字节流)
23 BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
24
25 // 封装图片文件对象(字节流)
26 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src//cn//itcast_13//mn.jpg"));
27
28 byte[] bys = new byte[1024];
29 int len = 0;
30 while ((len = bis.read(bys)) != -1) {
31 bos.write(bys, 0, len);
32 bos.flush(); // 刷新此输出流并强制写出所有缓冲的输出字节
33 }
34
35 // 给客户端一个反馈
36 OutputStream os = s.getOutputStream();
37 os.write("图片上传成功".getBytes());
38
39 bos.close();
40 s.close();
41 }
42 }
UploadServer.java
g:多线程改进上传文件
服务器的代码用线程进行封装(多线程),这样可以模拟一个同时接收多人上传文件的服务器。
(用循环也可以但是效率低,是单线程的程序)


1 package cn.itcast_15;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.FileReader;
6 import java.io.IOException;
7 import java.io.InputStreamReader;
8 import java.io.OutputStreamWriter;
9 import java.net.Socket;
10
11 public class UploadClient {
12 public static void main(String[] args) throws IOException {
13 // 创建客户端Socket对象
14 Socket s = new Socket("192.168.40.9", 11111);
15
16 // 封装文本文件
17 BufferedReader br = new BufferedReader(new FileReader("src//cn//itcast_15//a.txt"));
18
19 // 封装通道内的流对象
20 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
21
22 String line = null;
23 while ((line = br.readLine()) != null) { // 阻塞
24 bw.write(line);
25 bw.newLine();
26 bw.flush();
27 }
28
29 // Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了
30 s.shutdownOutput();
31
32 // 接收反馈
33 BufferedReader brClient = new BufferedReader(new InputStreamReader(
34 s.getInputStream()));
35 String client = brClient.readLine(); // 阻塞
36 System.out.println(client);
37
38 // 释放资源
39 br.close();
40 s.close();
41 }
42 }
UploadClient.java


1 package cn.itcast_15;
2
3 import java.io.IOException;
4 import java.net.ServerSocket;
5 import java.net.Socket;
6
7 public class UploadServer {
8 public static void main(String[] args) throws IOException {
9 // 创建服务器Socket对象
10 ServerSocket ss = new ServerSocket(11111);
11
12 while (true) {
13 Socket s = ss.accept(); // 监听客户端连接
14 new Thread(new UserThread(s)).start();
15 }
16 }
17
18 }
UploadServer.java


1 package cn.itcast_15;
2
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.FileWriter;
6 import java.io.IOException;
7 import java.io.InputStreamReader;
8 import java.io.OutputStreamWriter;
9 import java.net.Socket;
10
11 public class UserThread implements Runnable {
12
13 private Socket s;
14
15 public UserThread(Socket s) {
16 this.s = s;
17 }
18
19 @Override
20 public void run() {
21 try {
22 // 封装通道内的流对象
23 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
24
25 // 封装文本文件对象
26 // BufferedWriter bw = new BufferedWriter(new FileWriter("src//cn//itcast_15//Copy.java"));
27
28 // 为了防止名称冲突(即为了防止所有文件的名字都一样)
29 String newName = System.currentTimeMillis() + ".txt";
30 BufferedWriter bw = new BufferedWriter(new FileWriter("src//cn//itcast_15//" + newName));
31 // 如果在某一时间点,同时有很多人访问服务器,相同名字的文件也会出现很多,肿么办? 答:再加循环判断,一旦某个名字存在,就重新赋值另一名字即可。
32
33 String line = null;
34 while ((line = br.readLine()) != null) { // 阻塞
35 bw.write(line);
36 bw.newLine();
37 bw.flush();
38 }
39
40 // 给出反馈
41 BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
42 bwServer.write("文件上传成功");
43 bwServer.newLine();
44 bwServer.flush();
45
46 // 释放资源
47 bw.close();
48 s.close();
49 } catch (IOException e) {
50 e.printStackTrace();
51 }
52 }
53
54 }
UserThread.java
TCP传输容易出现的问题:
客户端连接上服务端,两端都在等待,没有任何数据传输。
通过例程分析:
因为read()方法或者readLine()方法是阻塞式。
解决办法:
1.自定义结束标记。
2.使用shutdownInput()、shutdownOutput()方法。
=============================================================================