一.通信的IO流了解
- 客户端和服务端用IO流交互
- 服务端使用客户端的IO流进行交互
二、练习1.实现TCP的相互通信代码实现
1. 客户端
Client用户.java
package Java学习.网络编程.TCP_UDP.TCP通信代码实现;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* 一、学习的2个类:
* 1.Client套接字: Socket 创建Socket 对象,发出OutputStream 请求 ,服务端回复后,InputStream 收到回复
* 创建Socket(String IP ,String port) 时候会自动和服务端Server3次握手建立联系
* 2.Server 服务套接字: new ServerSocket(port) 开启服务等待连接
* 二、步骤
* 1.服务器新建ServerSocket对象,打开端口,
* 2..accept()获取Socket开始监听客户端的请求
* 3.客户端新建Socket,指定IP和端口连接服务端
* 4.客户端使用getOutputStream 新建一个写入流准备写入字节到服务端
* 5.服务端结接收到打印信息回复客户端
* 6.客户端手接受来自服务端的信息,并打印
* 7.客户端关闭,Socket()
* 8.服务端关闭获取的Socket,然后关闭 ServerSocket
*/
public class Client用户 {
public static void main(String[] args) throws IOException {
//3.客户端新建Socket
Socket localhost = new Socket("127.0.0.1", 8888);
//4.客户端使用getOutputStream 新建一个写入流准备写入字节到服务端
OutputStream outputStream = localhost.getOutputStream();
//5 客户端调用outputStream.write()写入数据到服务端
outputStream.write("客户端:调用outputStream.write()写入数据到服务端".getBytes());
//6.客户端手接受来自服务端的信息,并打印
InputStream inputStream = localhost.getInputStream();
byte[] bytes2 = new byte[1024];
int length = inputStream.read(bytes2,0,bytes2.length);
System.out.println(new String(bytes2,0,length));
//7.客户端关闭,Socket()
localhost.close();
/**
* 问题:为什么接受不到服务端发送的信息呢???
*/
}
}
2. 服务端
Server服务端.java
package Java学习.网络编程.TCP_UDP.TCP通信代码实现;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 一、学习的2个类:
* 1.Client套接字: Socket 创建Socket 对象,发出OutputStream 请求 ,服务端回复后,InputStream 收到回复
* 创建Socket(String IP ,int port) 时候会自动和服务端Server3次握手建立联系
* 2.Server 服务套接字: new ServerSocket(port) 开启服务等待连接
* 二、步骤
* 1.服务器新建ServerSocket对象,打开端口,
* 2..accept()获取Socket开始监听客户端的请求
* 3.客户端新建Socket,指定IP和端口连接服务端
* 4.客户端使用getOutputStream 新建一个写入流准备写入字节到服务端
* 5.服务端结接收到打印信息回复客户端
* 6.客户端手接受来自服务端的信息,并打印
* 7.客户端关闭,Socket()
* 8.服务端关闭获取的Socket,然后关闭 ServerSocket
*/
public class Server服务器 {
public static void main(String[] args) throws IOException {
//1.服务器新建ServerSocket对象,打开端口,
ServerSocket serverSocket = new ServerSocket(8888);
//2.accept()获取Socket开始监听客户端的请求
Socket accept = serverSocket.accept();
//5.服务端结接收到打印信息回复客户端
InputStream inputStream = accept.getInputStream();
byte[] bytes = new byte[1024*10];
int length;
while ((length = inputStream.read(bytes,0,bytes.length)) != -1){
System.out.println(new String(bytes,0,length));
}
OutputStream outputStream = accept.getOutputStream();
outputStream.write("服务器: 已经收到信息".getBytes());
outputStream.flush();
//8.服务端关闭获取的Socket,然后关闭 ServerSocket
accept.close();
serverSocket.close();
}
}
3.操作步骤,先启动服务端,再启动用户端.
4. Run结果
问题??为什么服务端给用户的回信看不到????
1.原因
2. 解决方案.
三、练习2,
模拟TCP传输,
新增要求:
1.规范异常处理
2.使用管道流避免1024*n字节刚好断开字符导致的乱码的问题
1. Client客户端.java
package Java学习.网络编程.TCP_UDP.TCP通信代码实现.练习2新增规范异常处理和管道流;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
/**
* 一、步骤
* 1.客户端: new Socket(String ip,int port)
* 2.客户端: getOutputStream 然后发送信息给服务端Server
* 3.服务端 new ServerSocket(int port )
* 4.服务端 getInputStream客户端的读取数据,要
* 求读取时使用管道流字节数组输出流.ByteArraysOutputStream防止乱码
*/
public class Client客户端 {
public static void main(String[] args) {
Socket socket = null;
OutputStream outputStream = null;
try {
//1.客户端: new Socket(String ip,int port)
socket = new Socket("127.0.0.1", 8887);
//2.客户端: getOutputStream 然后发送信息给服务端Server
outputStream = socket.getOutputStream();
outputStream.write("客户端: 发送请求信息".getBytes());
} catch (IOException e) {
} finally {
//先开后关 (1)
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//先开后关(2)
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.Server服务端.java
package Java学习.网络编程.TCP_UDP.TCP通信代码实现.练习2新增规范异常处理和管道流;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 一、步骤
* 1.客户端: new Socket(String ip,int port)
* 2.客户端: getOutputStream 然后发送信息给服务端Server
* 3.服务端 new ServerSocket(int port ) 开放端口,并 accept() 获得 客户端的Socket()
* 4.服务端 getInputStream客户端的读取数据,要
* 求读取时使用管道流字节数组输出流.ByteArraysOutputStream防止乱码
* 二、注意原则,
* 1. 流先开后关,
* 2. 变量的声明和定义分离. ServerSocket serverSocket = null;
* 3.finally 用于关闭流,进行判断 if ( io != null) io.close()
*/
public class Server服务端 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream inputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
//3.服务端 new ServerSocket(int port ) 开放端口,并 accept() 获得 客户端的Socket()
serverSocket = new ServerSocket(8887);
socket = serverSocket.accept();
//4.服务端 getInputStream客户端的读取数据,要
inputStream = socket.getInputStream();
byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024 * 10];
int length;
while ((length = inputStream.read(bytes, 0, bytes.length)) != -1) {
byteArrayOutputStream.write(bytes, 0, length);
}
byte[] bytes1 = byteArrayOutputStream.toByteArray();
System.out.println("收到的信息为: " + (new String(bytes1)));
} catch (IOException e) {
} finally {
//先开后关(1)
if (byteArrayOutputStream != null) {
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//先开后关(2)
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//先开后关(3)
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//先开后关(4)
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
先打开服务端,再打开客户端,看代码。
Run:
四、练习3,TCP的文件的传输
1.目的:
- 一、目的:做一个服务器,实现
- 上传文件_服务器一直开_文件不覆盖_多线程
2.分析
- 二、步骤
- 1.客户端Socket 连接 服务端
- 2.客户端使用本地流,读取文件,使用网络流上传文件
-
- 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
- 4.服务端新建线程里,完成创建文件夹,文件的命名,网络流读取,本地流写入,并返回上传成功的提示
- 5.客户端接受上传成功的提示.
- 二、注意规范,
-
- 流先开后关,
-
- 变量的声明和定义分离. ServerSocket serverSocket = null;
- 3.finally 用于关闭流,进行判断 if ( io != null) io.close()
3.code
Client客户端3.java
package Java学习.网络编程.TCP_UDP.TCP通信代码实现.练习3上传文件_服务器一直开_文件不覆盖_多线程;
import java.io.*;
import java.net.Socket;
/**
* 一、目的:做一个服务器,实现
* 上传文件_服务器一直开_文件不覆盖_多线程
* 二、步骤
* 1.客户端Socket 连接 服务端
* 2.客户端使用本地流,读取文件,使用网络流上传文件
* 3. 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
* 4.服务端新建线程里,完成创建文件夹,文件的命名,网络流读取,本地流写入,并返回上传成功的提示
* 5.客户端接受上传成功的提示.
* 二、注意规范,
* 1. 流先开后关,
* 2. 变量的声明和定义分离. ServerSocket serverSocket = null;
* 3.finally 用于关闭流,进行判断 if ( io != null) io.close()
*/
public class Client客户端3 {
public static void main(String[] args) {
//声明定义分离
Socket socket = null;
FileInputStream fileInputStream = null;
OutputStream outputStream = null;
try {
//1.客户端Socket 连接 服务端
socket = new Socket("127.0.0.1", 8887);
//2.客户端使用本地流,读取文件,使用网络流上传文件
String url = "D:\\Program Files\\JetBrains\\test1\\Lab\\src\\Java学习\\网络编程\\TCP_" +
"UDP\\TCP通信代码实现\\练习3上传文件_服务器一直开_文件不覆盖_多线程\\";
fileInputStream = new FileInputStream(url + "img.png");
outputStream = socket.getOutputStream();
byte[] bytes = new byte[1024 * 10];
while (fileInputStream.read(bytes) != -1) {
outputStream.write(bytes);
}
//传输完成之后关闭网络输出流
socket.shutdownOutput();
//5.客户端接受上传成功的提示.
InputStream inputStream = socket.getInputStream();
//使用管道流防止字符乱码
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes1 = new byte[1024];
int length;
while ((length = inputStream.read(bytes1)) != -1) {
byteArrayOutputStream.write(bytes1, 0, length);
}
System.out.println("收到回复:" + (new String(byteArrayOutputStream.toByteArray())));
} catch (IOException e) {
e.printStackTrace();
} finally {
//先开后关(1)
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//先开后关(2)
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//先开后关(3)
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Server服务端3.java
package Java学习.网络编程.TCP_UDP.TCP通信代码实现.练习3上传文件_服务器一直开_文件不覆盖_多线程;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
/**
* 一、目的:做一个服务器,实现
* 上传文件_服务器一直开_文件不覆盖_多线程
* 二、步骤
* 1.客户端Socket 连接 服务端
* 2.客户端使用本地流,读取文件,使用网络流上传文件
* 3. 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
* 4.服务端新建线程里,完成创建文件夹,文件的命名,网络流读取,本地流写入,并返回上传成功的提示
* 5.客户端接受上传成功的提示.
* 二、注意规范,
* 1. 流先开后关,
* 2. 变量的声明和定义分离. ServerSocket serverSocket = null;
* 3.finally 用于关闭流,进行判断 if ( io != null) io.close()
*/
public class Server服务端3 {
public static void main(String[] args) {
//声明定义分离1
ServerSocket serverSocket = null;
Socket socket = null;
String url = "D:\\Program Files\\JetBrains\\test1\\Lab\\src\\Java学习\\网络编程\\TCP_UDP\\TCP" +
"通信代码实现\\练习3上传文件_服务器一直开_文件不覆盖_多线程\\imgs";
try {
//3. 服务端while(true)执行,如果accept()到一个Socket连接,新建一个线程,
serverSocket = new ServerSocket(8887);
System.out.println("服务端: 服务端启动成功");
while (true) {
socket = serverSocket.accept();
if (socket != null) {
Socket socketExist = socket;
new Thread(new Runnable() {
@Override
public void run() {
//新建一个线程
//声明定义分离 2
InputStream inputStream = null;
String fileName = null;
BufferedOutputStream bufferedOutputStream = null;
OutputStream outputStream = null;
try {
//4.服务端新建线程里,完成创建文件夹,文件的命名,网络流读取,本地流写入,并返回上传成功的提示
File file = new File(url);
if (!file.exists()) {//如果文件夹不存在.
file.mkdirs();//创建文件夹
}
inputStream = socketExist.getInputStream();
fileName = "\\" + socketExist.getInetAddress() + socketExist.getPort() + System.currentTimeMillis()+(new Random().nextInt(99999)) + ".png";
//网络流的读取,读取本地使用BufferOutputStream
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(url + fileName));
byte[] bytes = new byte[1024 * 100];
int length;
while ((length = inputStream.read(bytes)) != -1) {
bufferedOutputStream.write(bytes, 0, length);
}
System.out.println("服务端: 接受成功 "+fileName);
//并返回上传成功的提示
outputStream = socketExist.getOutputStream();
outputStream.write("服务端:您已经上传成功!".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//先进后出(2.1)
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//先进后出(2.2)
if (bufferedOutputStream != null) {
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//先进后出(2.3)
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//先进后出(1.1)
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//先进后出(1.2)
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Run:
(先打开服务端,再打开客户端,服务端不会停止,客户端可多次上传文件)
(Server服务端3的窗口)
服务端: 服务端启动成功
服务端: 接受成功 /127.0.0.111785158907230368282203.png
服务端: 接受成功 /127.0.0.111794158907231279174305.png
(Client客户端3的窗口)
收到回复:服务端:您已经上传成功!
Process finished with exit code 0
(过程展示)
五、练习4 BS TCP交互实现
1.目的
- 一、目的:做一个服务器,实现
- 上传文件_服务器一直开_文件不覆盖_多线程
- 客户端换为浏览器端
2.思路
- 一、步骤
-
- 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
-
- 新线程,接受信息,提取用用访问的文件的路径信息,
-
- 使用获取的页面的路径,是用本地流读取,使用HTTP协议网络流写入输出到浏览器,返回给客户端页面
- @HTTP协议 固定写法
-
outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
-
outputStream.write("Content-Type:text/html\r\n".getBytes());
//必须写入空行,不然浏览器不解析
-
outputStream.write("\r\n".getBytes());
- 二、问题:
-
- 浏览器需要刷新2次服务端才能得到信息.访问成功????,
- 首次输入连接不行,关闭连接或者刷新才能得到提交的信息,
- 删除while read,一次接受文件,会一次打印3次,提交的信息????
- 删除持续服务器的while true 无法打印连接后直接退出???
3.代码:
package Java学习.网络编程.TCP_UDP.TCP通信代码实现.练习4浏览器客户端;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 一、步骤
* 1. 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
* 2. 新线程,接受信息,提取用用访问的文件的路径信息,
* 3. 使用获取的页面的路径,是用本地流读取,使用HTTP协议网络流写入输出到浏览器,返回给客户端页面
* @HTTP协议 固定写法
* outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
* outputStream.write("Content-Type:text/html\r\n".getBytes());
* //比如写入空行,不然浏览器不解析
* outputStream.write("\r\n".getBytes());
*
* 二、问题:
* 1. 浏览器需要刷新2次服务端才能得到信息.访问成功????,
* 首次输入连接不行,关闭连接或者刷新才能得到提交的信息,
* 删除while read,一次接受文件,会一次打印3次,提交的信息????
* 删除持续服务器的while true 无法打印连接后直接退出???
*/
public class Server服务端4 {
public static void main(String[] args) {
//声明和定义分离 1
ServerSocket serverSocket = null;
Socket accept = null;
try {
serverSocket = new ServerSocket(8888);
System.out.println("服务端已经启动:" + serverSocket);
/**
* 1. 服务端while(true)执行,并accept()到一个Socket连接,新建一个线程,
*/
while (true) {
accept = serverSocket.accept();
System.out.println("连接成功! -- " + accept);
//新线程
if (accept != null) {
Socket finalAccept = accept;
new Thread(new Runnable() {
@Override
public void run() {
/**
* 2. 新线程,接受信息,提取用用访问的文件的路径信息,
* 提取用用访问的文件的路径信息:
* (1). BufferReader.readLine() 可读取第一行的有用信息。
* 构造 new BufferReader(new InputStreamReader(finalAccept.accept()))
* 因为只有 InputStreamReader 才能接受 网络流的InputStream 变为为
* 可被BufferReader 构造的 Reader
*/
//声明定义分离 2
InputStream inputStream = null;
BufferedReader bufferedReader = null;
try {
inputStream = finalAccept.getInputStream();
//提取有用信息
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String getUrl = bufferedReader.readLine().split(" ")[1];
String baseUrl ="D:/Program Files/JetBrains/test1/Lab";
String finalUrl = baseUrl +getUrl;
/**
* 3. 使用获取的页面的路径,是用本地流读取,
* 使用HTTP协议将:网络流写入输出到浏览器,返回给客户端页面
* HTTP协议响应头固定写法:
*
*/
FileInputStream fileInputStream = new FileInputStream(finalUrl);
OutputStream outputStream = finalAccept.getOutputStream();
/**
* HTTP 响应头
*/
outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
outputStream.write("Content-Type:text/html\r\n".getBytes());
//比如写入空行,不然浏览器不解析
outputStream.write("\r\n".getBytes());
byte[] bytes = new byte[1024 * 20];
int length = 0;
while ((length = fileInputStream.read(bytes))!= -1){
outputStream.write(bytes,0,length);
}
} catch (IOException e) {
} finally {
//(2.1)
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//(2.2)
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//(1.1)
if (accept != null) {
try {
accept.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//(1.2)
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8">
</head>
<body>
<h3>访问成功</h3>
</body>
</html>
- 运行服务端.
- 打开浏览器输入 你的网页的地址,我的为
127.0.0.1:8888/src/Java学习/网络编程/TCP_UDP/TCP通信代码实现/练习4浏览器客户端/index.html
理论因该可以实现,服务端接受到浏览器的访问,然后回馈index.html 页面的,
问题:待解决,
- 二、问题:
-
- 浏览器需要刷新2次服务端才能得到信息.访问成功????,
- 首次输入连接不行,关闭连接或者刷新才能得到提交的信息,
- 删除while read,一次接受文件,会一次打印3次,提交的信息????
- 删除持续服务器的while true 无法打印连接后直接退出???