C/S架构:Client/Server结构,是指客户端和服务器结构。常见程序有QQ、微信、迅雷等软件
B/S架构:Browser/Server结构,是指浏览器和服务器结构,常见浏览器有火狐、谷歌等。
两种架构各具优势,但是无论哪种架构都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机通信的程序。
网络通信协议:通信协议是计算机必须遵守的规则,只有遵守这些规则,计算机才能进行通信。协议中对数据的传输速率、传输格式、传输步骤等做了统一的对待,通信双方必须同时遵守,最终完成协议交换。
TCP/IP协议:传输控制协议/因特网互联协议,是因特网最基本、最广泛的协议,它定义了计算机如何连接因特网,以及数据如何在他们之间传输的标准,并采用了4层的分层模型。
java.net包中,提供了两种常见的网络协议支持:
传输控制协议。TCP是面向连接的通信协议,即传输数据前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
TCP三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠
第一次握手,客户端向服务器发出连接请求,等待服务器确认
第二次握手,服务器向客户端回送一个响应,通知客户端收到了连接请求
第三次握手,客户端再次向服务器发送确认信息,确认连接。
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全性。所以应用广泛,例如下载文件,浏览网页等,可靠效率不是很高
UDP:
用户数据报协议。UDP协议是一个面向无连接的协议。传输数据时,不需要建立连接,不管对方服务端是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。
每个数据包大小限制在64kb内。他是不可靠的协议,因为无连接,传输速度快,但是容易丢包。日常常用,视频会议、QQ聊天等。
计算机网络通信必须遵守的规则
互联网协议地址:俗称IP。
常用命令:ipconfig、ping
特殊IP:127.0.01、localhost
用两个字节表示的整数,取值范围(0~65535),其中0~1023之间的端口号用于一些知名的网络服务和应用,所以我们不能修改
普通的应用程序,需要使用1024以上的端口号,如果端口被占用,会导致当前程序启动失败。
常用的端口号:
1.80端口 网络端口
2.数据库: mysql 3306端口 , oracle 1521端口
3.Tomcat服务器:8080
TCP通信实现两台计算机之间的数据交互,通信的两端,要严格区分客户端(Client)和服务端(Server)
服务器端必须明确两件事:
1、多个客户端和服务器进行交互,服务器必须明确是和哪个客户端进行的交互
在服务器端,有一个方法accept,获取到请求的客户端对象
2、多个客户端和服务器进行交互,就需要使用多个IO流对象
服务器没有IO流。服务器可以获取请求的客户端对象Socket,使每个客户端Socket中提供的IO流和客户端进行交互
服务器使用客户端的字节输入流读取客户端发送的数据
服务器使用客户端的字节输出流给客户端回写数据
简单记:服务器使用客户端的流和客户端交互
ServerSocket 类:这个类实现了服务器套接字(包含了ip地址和端口号的网络单位),该对象等待通过网络的请求。
public Socket(String host,int port):创建套接字对象并将其连接到指定主机端口号,如果指定的host为null,则相当于指定地址为回送地址(127.0.0.1)
Socket client = new Socket("127.0.0.1",6666);
public InputStream getInputStream():返回此套接字输入流
public OutputStram getOutputStream():返回此套接字输出流
public void close():关闭此套接字
public void shutdownOutput():进制此套接字的输入流
例如:
package day13; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) throws IOException { //1、创建服务器ServerSocket对象和系统要指定的端口号 ServerSocket server = new ServerSocket(8888); System.out.println("服务器已启动"); //2、使用ServerSocket对象中的方法accpet,获取请求客户端对象Socket Socket socket = server.accept(); //4、使用Socket对象中的方法getInputStram(),获取网络字节输入流InputStram对象 InputStream is = socket.getInputStream(); //5、使用网络字节输入流InputStram对象中的方法read,读取服务器回写的数据 byte[] bytes = new byte[1024]; int len = is.read(bytes); System.out.println(new String(bytes,0,len)); //6、使用网络字节输出流OutputStram对象中的方法 write,给服务器发送数据 OutputStream os = socket.getOutputStream(); os.write("收到,谢谢".getBytes()); //7、释放资源(Socket ServerSocket) socket.close(); server.close(); } }Socket
注意:
1、客户端和服务器进行交互,必须使用Socket中提供的网络流,不能使用自己创建的对象
2、当我们创建客户端对象Socket的时候,就会去请求服务器并进行三次握手
3、如果服务器没启动,那么会抛出异常ConnectException
ServcerSocket()类:这个类实现了服务器套接字,该对象等待通过的网络请求
public ServerSocket(int port):使用该构造方法在创建ServerScoket对象时,将其绑定到一个指定端口,参数port就是端口
ServerScoket server = new ServerSocket(6666);
public Socket accpect():侦听并接收连接,返回一个新的Socket对象,用于和客户端实现通信。该方法回一致阻塞直到建立连接。
例如:
package day13; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) throws IOException { //1、创建服务器ServerSocket对象和系统要指定的端口号 ServerSocket server = new ServerSocket(8888); System.out.println("服务器已启动"); //2、使用ServerSocket对象中的方法accpet,获取请求客户端对象Socket Socket socket = server.accept(); //4、使用Socket对象中的方法getInputStram(),获取网络字节输入流InputStram对象 InputStream is = socket.getInputStream(); //5、使用网络字节输入流InputStram对象中的方法read,读取服务器回写的数据 byte[] bytes = new byte[1024]; int len = is.read(bytes); System.out.println(new String(bytes,0,len)); //6、使用网络字节输出流OutputStram对象中的方法 write,给服务器发送数据 OutputStream os = socket.getOutputStream(); os.write("收到,谢谢".getBytes()); //7、释放资源(Socket ServerSocket) socket.close(); server.close(); } }ServerSocket
package day13; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) throws IOException { //1、创建服务器ServerSocket对象和系统要指定的端口号 ServerSocket server = new ServerSocket(8888); System.out.println("服务器已启动"); //2、使用ServerSocket对象中的方法accpet,获取请求客户端对象Socket Socket socket = server.accept(); //4、使用Socket对象中的方法getInputStram(),获取网络字节输入流InputStram对象 InputStream is = socket.getInputStream(); //5、使用网络字节输入流InputStram对象中的方法read,读取服务器回写的数据 byte[] bytes = new byte[1024]; int len = is.read(bytes); System.out.println(new String(bytes,0,len)); //6、使用网络字节输出流OutputStram对象中的方法 write,给服务器发送数据 OutputStream os = socket.getOutputStream(); os.write("收到,谢谢".getBytes()); //7、释放资源(Socket ServerSocket) socket.close(); server.close(); } }Server
package day13; 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 { //1、创建一个客户端对象Socket,构造方法绑定服务器IP地址和端口号 Socket Client = new Socket("127.0.0.1",8888); //2、使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象 OutputStream os = Client.getOutputStream(); //3、使用网络字节输出流OutputStram对象中的方法 write,给服务器发送数据 os.write("你好服务器".getBytes()); //4、使用Socket对象中的方法getInputStram(),获取网络字节输入流InputStram对象 InputStream is = Client.getInputStream(); //5、使用网络字节输入流InputStram对象中的方法read,读取服务器回写的数据 byte[] bytes = new byte[1024]; int len = is.read(bytes); System.out.println(new String(bytes,0,len)); //6、释放资源Socket Client.close(); } }Client
package day13; import java.io.*; import java.net.Socket; public class demo01UpFileClient { public static void main(String[] args) throws IOException { //1、创建 FileInputStream fis = new FileInputStream("C:\\Users\\YIAN\\Desktop\\2.jpg"); //2、创建客户端Socket对象,构造方法绑定ip和端口 Socket socket = new Socket("127.0.0.1",8888); //3、使用Socket中的方法getOutputStram,获取网络字节输出流OutputStram对象 OutputStream os = socket.getOutputStream(); //4、使用本地字节输入流FileInputStram对象中的read方法 读取数据 int len = 0; byte[] bytes = new byte[1024]; while ((len=fis.read(bytes))!=-1){ //5、使用网络字节输出流OutputStram对象中的方法,把读取的文件上传服务器 os.write(bytes,0,len); } //重点:禁用此套接字输出流,不禁用 输入流会进入阻塞状态 socket.shutdownOutput(); //6、使用Socket中的方法getInputStream获取网络字节输入流 InputStream is = socket.getInputStream(); while ((len=is.read(bytes))!=-1){ //7、使用网络网络字节输入流对象中的read方法读取服务器回写数据 System.out.println(new String(bytes,0,len)); } //8、关闭资源 fis.close(); socket.close(); } }Client
package day13; import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class demo01UpFileServer { public static void main(String[] args) throws IOException { //1、创建服务器对象,向系统要指定端口号 ServerSocket server = new ServerSocket(8888); //2、通过ServerSocket对象中的方法 accept,获取请求的客户端Socket对象 Socket client = server.accept(); //3、使用Socket对象的 getInputStram方法 获取网络字节输入流 InputStream is = client.getInputStream(); //4、创建保存的路径 File file = new File("C:\\Users\\YIAN\\Desktop\\File\\upload\\upload\\"); //判断路径是否存在 不存在则创建指定文件 if(!file.exists()){ file.mkdirs(); } //5、创建本地字节输出流,构造方法绑定输出的目的地 FileOutputStream fos = new FileOutputStream(file+"\\upload.jpg"); //6、使用网络字节输入流中的read方法,读取客户端上传的文件 int len = 0; byte[] bytes = new byte[1024]; while ((len=is.read(bytes))!=-1){ //7、使用本地字节输入流中的write方法,将读取的文件 存储到指定地址 fos.write(bytes,0,len); } //8、使用网络输出流对象,给客户端回写“上传成功” client.getOutputStream().write("上传成功".getBytes()); //9、释放资源 fos.close(); client.close(); server.close(); } }Server
注意:上述客户端不加ShutdownOutput(),服务器和客户端会一直运行,不加会造成IS中的read()阻塞
服务端保存文件时,名称写死,那么服务器始终只会保存最新存储的文件,所以建议使用系统时间优化,保证文件名的唯一性
package day13; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Calendar; import java.util.Date; public class demo01UpFileServer { public static void main(String[] args) throws IOException { //1、创建服务器对象,向系统要指定端口号 ServerSocket server = new ServerSocket(8888); //2、通过ServerSocket对象中的方法 accept,获取请求的客户端Socket对象 Socket client = server.accept(); //3、使用Socket对象的 getInputStram方法 获取网络字节输入流 InputStream is = client.getInputStream(); //4、创建保存的路径 File file = new File("C:\\Users\\YIAN\\Desktop\\File\\upload\\upload\\"); //判断路径是否存在 不存在则创建指定文件 if(!file.exists()){ file.mkdirs(); } //5、创建本地字节输出流,构造方法绑定输出的目的地 //时间优化文件唯一性 FileOutputStream fos = new FileOutputStream(file+"\\"+System.currentTimeMillis()+"upload.jpg"); //6、使用网络字节输入流中的read方法,读取客户端上传的文件 int len = 0; byte[] bytes = new byte[1024]; while ((len=is.read(bytes))!=-1){ //7、使用本地字节输入流中的write方法,将读取的文件 存储到指定地址 fos.write(bytes,0,len); } //8、使用网络输出流对象,给客户端回写“上传成功” client.getOutputStream().write("上传成功".getBytes()); //9、释放资源 fos.close(); client.close(); server.close(); } }Server(优化:文件名唯一性)
package day13; import java.io.*; import java.net.Socket; public class demo01UpFileClient { public static void main(String[] args) throws IOException { //1、创建 FileInputStream fis = new FileInputStream("C:\\Users\\YIAN\\Desktop\\2.jpg"); //2、创建客户端Socket对象,构造方法绑定ip和端口 Socket socket = new Socket("127.0.0.1",8888); //3、使用Socket中的方法getOutputStram,获取网络字节输出流OutputStram对象 OutputStream os = socket.getOutputStream(); //4、使用本地字节输入流FileInputStram对象中的read方法 读取数据 int len = 0; byte[] bytes = new byte[1024]; while ((len=fis.read(bytes))!=-1){ //5、使用网络字节输出流OutputStram对象中的方法,把读取的文件上传服务器 os.write(bytes,0,len); } //重点:禁用此套接字输出流,不禁用 输入流会进入阻塞状态 socket.shutdownOutput(); //6、使用Socket中的方法getInputStream获取网络字节输入流 InputStream is = socket.getInputStream(); while ((len=is.read(bytes))!=-1){ //7、使用网络网络字节输入流对象中的read方法读取服务器回写数据 System.out.println(new String(bytes,0,len)); } //8、关闭资源 fis.close(); socket.close(); } }Client
服务端,只保存一个文件就关闭了,之后用户无法上传数据(不符合实际),使用循环改进,可以让服务器不断接收不同用户的文件
package day13; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Calendar; import java.util.Date; public class demo01UpFileServer { public static void main(String[] args) throws IOException { //1、创建服务器对象,向系统要指定端口号 ServerSocket server = new ServerSocket(8888); System.out.println("服务器已启动..."); //优化服务器只能上传一次的问题 while (true) { //2、通过ServerSocket对象中的方法 accept,获取请求的客户端Socket对象 Socket client = server.accept(); //3、使用Socket对象的 getInputStram方法 获取网络字节输入流 InputStream is = client.getInputStream(); //4、创建保存的路径 File file = new File("C:\\Users\\YIAN\\Desktop\\File\\upload\\upload\\"); //判断路径是否存在 不存在则创建指定文件 if (!file.exists()) { file.mkdirs(); } //5、创建本地字节输出流,构造方法绑定输出的目的地 //时间优化文件唯一性 long time = System.currentTimeMillis(); FileOutputStream fos = new FileOutputStream(file + "\\" + System.currentTimeMillis() + ".jpg"); //6、使用网络字节输入流中的read方法,读取客户端上传的文件 int len = 0; byte[] bytes = new byte[1024]; while ((len = is.read(bytes)) != -1) { //7、使用本地字节输入流中的write方法,将读取的文件 存储到指定地址 fos.write(bytes, 0, len); } //8、使用网络输出流对象,给客户端回写“上传成功” client.getOutputStream().write("上传成功".getBytes()); System.out.println(time+".jpg文件已成功保存"); //9、释放资源 fos.close(); client.close(); } } }Server(优化:服务器只能上传一次问题)
package day13; import java.io.*; import java.net.Socket; public class demo01UpFileClient { public static void main(String[] args) throws IOException { //1、创建 FileInputStream fis = new FileInputStream("C:\\Users\\YIAN\\Desktop\\2.jpg"); //2、创建客户端Socket对象,构造方法绑定ip和端口 Socket socket = new Socket("127.0.0.1",8888); //3、使用Socket中的方法getOutputStram,获取网络字节输出流OutputStram对象 OutputStream os = socket.getOutputStream(); //4、使用本地字节输入流FileInputStram对象中的read方法 读取数据 int len = 0; byte[] bytes = new byte[1024]; while ((len=fis.read(bytes))!=-1){ //5、使用网络字节输出流OutputStram对象中的方法,把读取的文件上传服务器 os.write(bytes,0,len); } //重点:禁用此套接字输出流,不禁用 输入流会进入阻塞状态 socket.shutdownOutput(); //6、使用Socket中的方法getInputStream获取网络字节输入流 InputStream is = socket.getInputStream(); while ((len=is.read(bytes))!=-1){ //7、使用网络网络字节输入流对象中的read方法读取服务器回写数据 System.out.println(new String(bytes,0,len)); } //8、关闭资源 fis.close(); socket.close(); } }Client
服务端在接收大文件时,可能耗费几秒或几分钟的时间,此时不能接收其他用户上传的文件,所以可以使用多线程优化代码
package day13; import java.io.*; import java.net.ServerSocket; import java.net.Socket; import java.util.Calendar; import java.util.Date; public class demo01UpFileServer { public static void main(String[] args) throws IOException { //1、创建服务器对象,向系统要指定端口号 ServerSocket server = new ServerSocket(8888); System.out.println("服务器已启动..."); //优化服务器只能上传一次的问题 while (true) { //2、通过ServerSocket对象中的方法 accept,获取请求的客户端Socket对象 Socket client = server.accept(); //优化效率 多线程 new Thread(()->{ try { //3、使用Socket对象的 getInputStram方法 获取网络字节输入流 InputStream is = client.getInputStream(); //4、创建保存的路径 File file = new File("C:\\Users\\YIAN\\Desktop\\File\\upload\\upload\\"); //判断路径是否存在 不存在则创建指定文件 if (!file.exists()) { file.mkdirs(); } //5、创建本地字节输出流,构造方法绑定输出的目的地 //时间优化文件唯一性 long time = System.currentTimeMillis(); FileOutputStream fos = new FileOutputStream(file + "\\" + System.currentTimeMillis() + ".jpg"); //6、使用网络字节输入流中的read方法,读取客户端上传的文件 int len = 0; byte[] bytes = new byte[1024]; while ((len = is.read(bytes)) != -1) { //7、使用本地字节输入流中的write方法,将读取的文件 存储到指定地址 fos.write(bytes, 0, len); } //8、使用网络输出流对象,给客户端回写“上传成功” client.getOutputStream().write("上传成功".getBytes()); System.out.println(time+".jpg文件已成功保存"); fos.close(); client.close(); }catch (IOException E){ System.out.println(E); } }).start(); } } }Server(优化效率问题)
package day13; import java.io.*; import java.net.Socket; public class demo01UpFileClient { public static void main(String[] args) throws IOException { //1、创建 FileInputStream fis = new FileInputStream("C:\\Users\\YIAN\\Desktop\\2.jpg"); //2、创建客户端Socket对象,构造方法绑定ip和端口 Socket socket = new Socket("127.0.0.1",8888); //3、使用Socket中的方法getOutputStram,获取网络字节输出流OutputStram对象 OutputStream os = socket.getOutputStream(); //4、使用本地字节输入流FileInputStram对象中的read方法 读取数据 int len = 0; byte[] bytes = new byte[1024]; while ((len=fis.read(bytes))!=-1){ //5、使用网络字节输出流OutputStram对象中的方法,把读取的文件上传服务器 os.write(bytes,0,len); } //重点:禁用此套接字输出流,不禁用 输入流会进入阻塞状态 socket.shutdownOutput(); //6、使用Socket中的方法getInputStream获取网络字节输入流 InputStream is = socket.getInputStream(); while ((len=is.read(bytes))!=-1){ //7、使用网络网络字节输入流对象中的read方法读取服务器回写数据 System.out.println(new String(bytes,0,len)); } //8、关闭资源 fis.close(); socket.close(); } }Client
package day13.demo; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(8806); System.out.println("服务器 启动...."); //循环接收 while(true){ //建立连接 Socket client = server.accept(); new Thread(()->{ try { //使用Socket对象中的方法getInputStram(),获取网络字节输入流InputStram对象 InputStream is = client.getInputStream(); //使用网络字节输入流InputStram对象中的方法read,读取服务器回写的数据 int len = 0; byte[] bytes = new byte[1024]; while ((len=is.read(bytes))!=-1){ System.out.println(new String (bytes,0,len)); } //使用网络字节输出流OutputStram对象中的方法 write,给服务器发送数据 OutputStream os = client.getOutputStream(); os.write("您已上线".getBytes()); System.out.println("有新用户上线"); is.close(); os.close(); }catch (IOException e){ System.out.println(e); } }).start(); } } }TCPserver
package day13.demo; 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 { Socket socket = new Socket("127.0.0.1",8806); OutputStream os = socket.getOutputStream(); int len = 0; byte[] bytes = new byte[1024]; os.write("请求上线".getBytes()); socket.shutdownOutput(); InputStream is = socket.getInputStream(); while ((len=is.read(bytes))!=-1){ System.out.println(new String (bytes,0,len)); } os.close(); is.close(); socket.close(); } }Client