网络编程
软件架构
- C/S : 客户端/服务器架构 , 例如:
QQ
,迅雷,微信,腾讯视频,爱奇艺其… - B/S: 浏览器/服务器架构,例如:所有借助浏览器的应用都属于B/S, 淘宝网站,京东网站,公司门户网站…
网络通信协议
网络通信协议:就是网络数据传输的一种规范,规则。
TCP
:面向连接,只有建立了逻辑上连接(三次握手),才能够进行通信,传输数据安全可靠。例如:下载文件,网页浏览UDP
:面向无连接。 传输不安全,但是数据传输效率快。 例如:视频会议…
网络通信3要素
- 协议:数据传输的规范
-
IP
地址: 找到网络设备,比如某台电脑 - 端口号:找到某台设备中运行的进程,端口号定义范围[0,65535],[0,1023]系统服务使用,如果要自己定义端口号,需要1024以上的端口号。同一台设备中,不同的进程,端口号一定是不一致。
TCP编程
1:服务端和客户端的介绍
服务端:ServerSocket
客户端:Socket
2:客户端Socket类
客户端Socket类创建对象,可以连接指定地址,指定端口号的服务端。
- 构造方法
public Socket(String host , int port)
host:服务端地址
port:服务端程序的端口号
协议不用管,底层封装好了
- 成员方法
public OutputStream getOutputStream() 获取一个网络输出流,用来写数据到服务端
public InpuptStream getInputStream()获取一个网络的输入流,用来读取服务端回传的数据
public void close() 关闭客户端和服务端连接
public void shutdownOutput() 关闭输出流,socket连接不会关闭。一般是在写出数据完毕之后,调用。
3:服务端ServerSocket
类
服务端ServerSocket
类,用来接收客户端的连接,提供服务。
- 构造方法
public ServerSocket(int port)
port: 就是当前服务端程序的端口号,供客户端连接使用
- 成员方法
public Socket accept() 当调用这个方法时,会进入阻塞状态,等待客户端连接。返回一个Socket对象,用来和客户端的Socket对象进行交互。
服务端返回的Socket对象使用和客户端Socket对象使用,一样的。
4:实现简单的客户端和服务端通信
开发原理图,
注意:开发好服务端和客户端后,先开启服务端程序。
客户端代码实现
public class Client {
public static void main(String[] args) throws IOException {
//1.建立Socket对象指定服务端ip和端口
Socket socket = new Socket("127.0.0.1", 8888);
//2.获取网络输出流,进行发送数据给务端端
OutputStream netOut = socket.getOutputStream();
netOut.write("你好呀!吃饭了啊【来自客户端的问候】".getBytes());
//3.调用shutDownOutputStream,告诉服务端传输完毕
socket.shutdownOutput();
//4.获取网络输入流用来读取服务端回传的数据
InputStream netIn = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
while ((len = netIn.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, len));
}
//5.关闭服务
socket.close();
}
}
服务端代码实现
/*
服务端
*/
public class Server {
public static void main(String[] args) throws IOException {
//1.创建ServerSocket对象,绑定一个端口 8888
ServerSocket serverSocket = new ServerSocket(8888);
//2.调用accept方法接受客户端的连接【阻塞效果】
System.out.println("等待客户端连接.....");
Socket socket = serverSocket.accept();
System.out.println("已经有一个客户端连接....");
//得到一个用于服务客户端的Socket对象
//3.获取网络输入流:读取客户端发送过来数据
InputStream netIn = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
while ((len = netIn.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, len));
}
//4.获取网络输出流:响应数据到客户端
OutputStream netOut = socket.getOutputStream();
netOut.write("你也好呀!!我吃过了【响应自服务户端】".getBytes());
//5.调用shutDownOutputStream,告诉客户端传输完毕
socket.shutdownOutput();
//6.关闭资源
socket.close();
serverSocket.close();//真实开发中,不会关
}
}
文件上传案例
文件上传原理分析图:
客户端开发
读取本地文件到内存,使用网络输出流写出到网络中【服务端接收】
1)先把Socket对象创建,绑定服务端第的地址和端口
2)获取网络输出流,准备输出数据
3)创建本地的输入流,将文件读取到内存
4)边读数据,边写出到网络
5)写完后告诉服务端,数据传输完毕
6)接收服务端的结果响应
7)结束
代码实现:
public class Client {
public static void main(String[] args) throws IOException {
//1)先把Socket对象创建,绑定服务端第的地址和端口
Socket socket = new Socket("127.0.0.1", 8888);
//2)获取网络输出流,准备输出数据
OutputStream netOut = socket.getOutputStream();
//3)创建本地的输入流,将文件读取到内存
FileInputStream localIn = new FileInputStream("client/美女.jpg");
//4)边读数据,边写出到网络
byte[] bytes = new byte[1024];
int len;
while ((len = localIn.read(bytes)) != -1) {
//边读取本地数据,边写出到网络中
netOut.write(bytes, 0, len);
}
//5)写完后告诉服务端,数据传输完毕
socket.shutdownOutput();
//6)接收服务端的结果响应
InputStream netIn = socket.getInputStream();
while ((len = netIn.read(bytes)) != -1) {
System.out.println(new String(bytes, 0, len));
}
//7)结束
localIn.close();
socket.close();//可以将网络流同时关闭
}
}
服务端程序开发
服务端通过网络的输入流获取客户端的数据,并将数据写到硬盘
1)先创建Server Socket对象,绑定端口号 8888
2)接受客户端的连接,获取对应的Socket对象
3)获取网络输入流,读取客户端上传的数据
4)创建本地的输出流,将读取的数据写出到文件中
5)边读,边写
6)获取网络输出流,告诉客户端上传成功
7)告诉客户端数据发送完毕
8)关闭资源
代码实现
public class Server {
public static void main(String[] args) throws IOException {
//1)先创建Server Socket对象,绑定端口号 8888
ServerSocket serverSocket = new ServerSocket(8888);
//2)接受客户端的连接,获取对应的Socket对象
System.out.println("等待客户端连接...");
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功!!");
//3)获取网络输入流,读取客户端上传的数据
InputStream netIn = socket.getInputStream();
//4)创建本地的输出流,将读取的数据写出到文件中
FileOutputStream localOut = new FileOutputStream("server/beauty.jpg");
//5)边读,边写
byte[] bytes = new byte[1024];
int len;
while ((len = netIn.read(bytes)) != -1) {
localOut.write(bytes, 0, len);
}
//6)获取网络输出流,告诉客户端上传成功
OutputStream netOut = socket.getOutputStream();
netOut.write("恭喜您!文件上传成功!".getBytes());
//7)告诉客户端数据发送完毕
socket.shutdownOutput();
//8)关闭资源
localOut.close();
socket.close();//只能关闭网络流
serverSocket.close();
}
}
服务端开发优化1
改造为可以循环接受客户端连接,并文件不会覆盖的代码
/*
优化:
1.能让服务循环服务
2.上传的文件不能覆盖
*/
public class Server {
public static void main(String[] args) throws IOException {
//1)先创建Server Socket对象,绑定端口号 8888
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
//2)接受客户端的连接,获取对应的Socket对象
System.out.println("等待客户端连接...");
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功!!");
//3)获取网络输入流,读取客户端上传的数据
InputStream netIn = socket.getInputStream();
//4)创建本地的输出流,将读取的数据写出到文件中
long time = System.currentTimeMillis();
FileOutputStream localOut = new FileOutputStream("server/beauty" + time + ".jpg");
//5)边读,边写
byte[] bytes = new byte[1024];
int len;
while ((len = netIn.read(bytes)) != -1) {
localOut.write(bytes, 0, len);
}
//6)获取网络输出流,告诉客户端上传成功
OutputStream netOut = socket.getOutputStream();
netOut.write("恭喜您!文件上传成功!".getBytes());
//7)告诉客户端数据发送完毕
socket.shutdownOutput();
//8)关闭资源
localOut.close();
socket.close();//只能关闭网络流
}
//serverSocket.close();
}
}
服务端开发优化2
借助多线程实现
/*
优化:
线程优化,能够让多个客户端并发上传数据
*/
public class Server {
public static void main(String[] args) throws IOException {
//1)先创建Server Socket对象,绑定端口号 8888
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
//2)接受客户端的连接,获取对应的Socket对象
System.out.println("等待客户端连接...");
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功!!");
//每获取一个socket连接都会开启一个线程用来服务
new Thread(() -> {
try {
//3)获取网络输入流,读取客户端上传的数据
InputStream netIn = socket.getInputStream();
//4)创建本地的输出流,将读取的数据写出到文件中
long time = System.currentTimeMillis();
FileOutputStream localOut = new FileOutputStream("server/beauty" + time + ".jpg");
//5)边读,边写
byte[] bytes = new byte[1024];
int len;
while ((len = netIn.read(bytes)) != -1) {
localOut.write(bytes, 0, len);
}
//6)获取网络输出流,告诉客户端上传成功
OutputStream netOut = socket.getOutputStream();
netOut.write("恭喜您!文件上传成功!".getBytes());
//7)告诉客户端数据发送完毕
socket.shutdownOutput();
//8)关闭资源
localOut.close();
socket.close();//只能关闭网络流
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
//serverSocket.close();
}
}
BS服务端开发
原理分析:
服务器端开发
- 准备数据,资料中由一个web文件夹拷贝到根目录
- 开发服务端(文件上传的案例类似)
注意:
- 获取浏览器访问时,需要解析浏览器所要的资源
会收到浏览器访问的数据: http协议数据
GET /web/index.html HTTP/1.1
....
....
传过来的第一行数据中存在,
1:请求的方式:GET (POST PUT..)
2:浏览器需要的资源:web/index.html
3:协议的版本:HTTP/1.1
- 将浏览器所需的资源,通过网络输出流输出到网络中,写出数据时,先写协议,浏览器才能够认识。
netOut.write("HTTP/1.1 200 OK\r\n".getBytes());
netOut.write("Content-Type:text/html\r\n".getBytes());
netOut.write("\r\n".getBytes()); //第三行空行
- 访问自己的服务器:
http://127.0.0.1:7788/web/index.html
,如果要访问同桌的服务器,直接更换IP地址
代码实现
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8899);
while (true) {
//等待浏览器访问
System.out.println("等待浏览器访问....");
Socket socket = serverSocket.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
//获取浏览器想要的资源
String path = getPath(socket);
if (path == null || path.equals("favicon.ico")) {
System.out.println("没有文件....");
socket.close();
//continue; while循环中
return;
}
//获取网络输出流将资源传输给浏览器
OutputStream netOut = socket.getOutputStream();
//获取本地的输入流,将资源读取到内存中
FileInputStream localIn = new FileInputStream(path);
//写协议
netOut.write("HTTP/1.1 200 OK\r\n".getBytes());
netOut.write("Content-Type:text/html\r\n".getBytes());
netOut.write("\r\n".getBytes()); //第三行空行
//边读边写
int len;
byte[] bytes = new byte[1024];
while ((len = localIn.read(bytes)) != -1) {
netOut.write(bytes, 0, len);
}
socket.shutdownOutput();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
public static String getPath(Socket socket) throws IOException {
//获取网络输入流,用来接收浏览器发送过来的数据
InputStream netIn = socket.getInputStream();
//把输入流转化成字符流
InputStreamReader isr = new InputStreamReader(netIn);
//把上面的字符流包装成高效流
BufferedReader br = new BufferedReader(isr);
//读取http协议的第一行数据:GET /web/index.html HTTP/1.1
String firstLine = br.readLine();
if (firstLine == null) {
return null;
}
String path = firstLine.split(" ")[1].substring(1); //web/index.html}
return path;
}
}