软件结构:
C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、微信等软件。
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。
两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机 的通信的程序。
网络通信协议:
网络通信协议:通信协议是对计算机必须遵守的规则,只有遵守这些规则,计算机之间才能进行通信。协议中对数据的传输格式、传输速率、传输步骤等做了 统一规定,通信双方必须同时遵守,最终完成数据交换。
TCP/IP协议: 传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),是 Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它 的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的 协议来完成自己的需求。
协议的分类
TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前, 在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
TCP三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可 靠。 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。 第三次握手,客户端再次向服务器端发送确认信息,确认连接。
UDP:用户数据报协议(User Datagram Protocol)。UDP协议是一个面向无连接的协议。传输数据时,不需 要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个 数据包的大小限制在64k以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应 用中,例如视频会议、QQ聊天等。
概述:
网络编程三大要素:协议,IP,端口号。
下图模拟网络编程的基本行为:
此图来自https://www.bilibili.com/video/BV1T7411m7Ta
1.C/S结构(客户端/服务器)
1.1 定义客户端
package Socket.fileup2;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class TCPclient {
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("G:\\我是GBK编码文件转换的UTF-8文件.txt");
Socket socket=new Socket("127.0.0.1",8888);
OutputStream outputStream = socket.getOutputStream();
int len;
byte []b=new byte[1024];
while ((len=fis.read(b))!=-1)
{
outputStream.write(b,0,len);
}
socket.shutdownOutput();
InputStream inputStream = socket.getInputStream();
while ((len=inputStream.read(b))!=-1)
{
System.out.println(new String(b,0,len));
}
fis.close();
socket.close();
}
}
1.2 定义服务器端
package Socket.fileup2;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
public class TCPserver {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket=new ServerSocket(8888);
while (true){
Socket socket = serverSocket.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
File file=new File("G:\\serversocketfile");
if(!file.exists()){
file.mkdir();
}
byte[] b=new byte[1024];
//发现问题:如果有多个文件上传就会导致文件重名,那么可以用域名+毫秒+随机数来命名规则这样就能基本解决文件重名问题了
String fe="kuangkuang"+System.currentTimeMillis()+new Random(100000).nextInt()+".txt";
FileOutputStream fos=new FileOutputStream(file+"\\"+fe);
// OutputStreamWriter fos=new OutputStreamWriter(new FileOutputStream(file+"\\serversocketfile.txt"),"UTF-8");
int len=0;
while ((len=inputStream.read(b))!=-1)
{
System.out.println(len);
fos.write(b,0,len);
}
// OutputStream outputStream = socket.getOutputStream();
// outputStream.write("已保存".getBytes());
socket.getOutputStream().write("已保存".getBytes());
socket.close();
fos.close();
}catch (IOException e){
e.printStackTrace();
}
}
}).start();
}
//服务器就不用关闭,因为需要一直运行
//serverSocket.close();
}
}
服务器是没有流的概念的,需要通过客户端进行
socket.getInputStream()
方法获取流,最后文件关闭需要依次由小到大进行关闭。
如果有多个文件上传就会导致文件重名,那么可以用域名+毫秒+随机数来命名规则这样就能基本解决文件重名问题了。
死循环是为了能让服务器一直运行接收各个不同客户端的请求进行解决,而多线程能保证程序的安全性和效率
运行结果:
可以看到文件夹(服务器)已经存有客户端上传的文件,并且返回确认信息被客户端进行接收打印。
2.B/S结构(浏览器/服务器)
由于软件环境问题这里就只演示代码粘贴了
浏览器:
package Socket.TCPBS;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*
创建BS版本TCP服务器
*/
public class TCPServerThread {
public static void main(String[] args) throws IOException {
//创建一个服务器ServerSocket,和系统要指定的端口号
ServerSocket server = new ServerSocket(8080);
/*
浏览器解析服务器回写的html页面,页面中如果有图片,那么浏览器就会单独的开启一个线程,读取服务器的图片
我们就的让服务器一直处于监听状态,客户端请求一次,服务器就回写一次
*/
while(true){
//使用accept方法获取到请求的客户端对象(浏览器)
Socket socket = server.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
//使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//使用网络字节输入流InputStream对象中的方法read读取客户端的请求信息
/*byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}*/
//把is网络字节输入流对象,转换为字符缓冲输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//把客户端请求信息的第一行读取出来 GET /11_Net/web/index.html HTTP/1.1
String line = br.readLine();
System.out.println(line);
//把读取的信息进行切割,只要中间部分 /11_Net/web/index.html
String[] arr = line.split(" ");
//把路径前边的/去掉,进行截取 11_Net/web/index.html
String htmlpath = arr[1].substring(1);
//创建一个本地字节输入流,构造方法中绑定要读取的html路径
FileInputStream fis = new FileInputStream(htmlpath);
//使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
// 写入HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
// 必须要写入空行,否则浏览器不解析
os.write("\r\n".getBytes());
//一读一写复制文件,把服务读取的html文件回写到客户端
int len = 0;
byte[] bytes = new byte[1024];
while((len = fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
//释放资源
fis.close();
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}).start();
}
//server.close();
}
}
服务器
package Socket.TCPBS;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/*
创建BS版本TCP服务器
*/
public class TCPServer {
public static void main(String[] args) throws IOException {
//创建一个服务器ServerSocket,和系统要指定的端口号
ServerSocket server = new ServerSocket(8080);
//使用accept方法获取到请求的客户端对象(浏览器)
Socket socket = server.accept();
//使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//使用网络字节输入流InputStream对象中的方法read读取客户端的请求信息
/*byte[] bytes = new byte[1024];
int len = 0;
while((len = is.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}*/
//把is网络字节输入流对象,转换为字符缓冲输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//把客户端请求信息的第一行读取出来 GET /11_Net/web/index.html HTTP/1.1
String line = br.readLine();
//把读取的信息进行切割,只要中间部分 /11_Net/web/index.html
String[] arr = line.split(" ");
//把路径前边的/去掉,进行截取 11_Net/web/index.html
String htmlpath = arr[1].substring(1);
//创建一个本地字节输入流,构造方法中绑定要读取的html路径
FileInputStream fis = new FileInputStream(htmlpath);
//使用Socket中的方法getOutputStream获取网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
// 写入HTTP协议响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-Type:text/html\r\n".getBytes());
// 必须要写入空行,否则浏览器不解析
os.write("\r\n".getBytes());
//一读一写复制文件,把服务读取的html文件回写到客户端
int len = 0;
byte[] bytes = new byte[1024];
while((len = fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
//释放资源
fis.close();
socket.close();
server.close();
}
}