更多精彩尽在博主首页:i新木优子
Java基础系列教程:传送门
♂️寄语:不经历风雨,长不成大树,不受百炼,难以成钢
欢迎关注点赞收藏⭐留言
作者水平有限,发现错误欢迎留言轰炸
网络编程就是要实现在网络中不同计算机之间的通信
如何实现不同计算机之间的数据传输呢?
计算机之间的传输就和我们现实中的收发快递是一样的,首先就要知道收件人的地址,在网络传输中就是目标计算机的地址也就是常说的IP地址(唯一且不重复的),快递送到地址之后还要正确的送到收件人手中,对应的就计算机中的某一个应用程序也就是端口号(port),在快递运送的时候我们可以选择不同的快递公司,在网络中就是网络通信协议。网络传输三要素:
网络通信协议:
- 定义:通过计算机网络可以使多台计算机实现连接,但是位于同一个网络中的计算机在进行连接和通信时必须要遵守一定的规则。在计算机网络中,这些连接和通信的规则被称为网络通信协议。
作用:它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交互。- 分类:目前应用最广泛的有TCP/IP协议(Transmission Control Protocol/Internet Protocol,传输控制协议/英特网互联协议)、UDP协议(User Datagram Protocol,用户数据报协议)和其他一些协议的协议组。
UDP协议:
- 定义:UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接
说明:- 使用UDP协议消耗资源小、通信效率高、延迟小,所以通常都会用于音频、视
频和普通数据的传输,例如视频会议都使用UDP协议。- 使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整
性,因此在传输重要数据时不建议使用UDP协议。- UDP传输时数据包大小不能超过64K
UDP传输发送方的步骤:
- 1️⃣建立UDP的服务
- 2️⃣打包
- 3️⃣发送
- 4️⃣关闭
UDP接收方的步骤:
- 1️⃣创建UDP服务,绑定端口
- 2️⃣定义数据包
- 3️⃣接收
- 4️⃣拆包
Java在网络传输中自己就封装好了类,直接调用即可,不理解的小伙伴可以自己查看一下api文档哦
UDP发送端代码:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/*
* UDP的发送
*
* 将“hello i新木优子”发送给计算机“192.168.56.1”的10001端口的程序
*/
public class TestUDPSend {
public static void main(String[] args) throws Exception {
System.out.println("发送端启动:");
//[1]建立UDP的服务
DatagramSocket ds=new DatagramSocket();//发送方不用参数
//[2]打包
String data="hello i新木优子";
byte[]buf=data.getBytes();
InetAddress ip=InetAddress.getByName("192.168.56.1");//IP地址
int port=10001;//端口号
DatagramPacket dp=new DatagramPacket(buf, buf.length, ip, port);//发送的数据包,4个参数
//[3]发送
ds.send(dp);
//[4]关闭
ds.close();
System.out.println("发送结束");
}
}
UDP接收端代码:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/*
* UDP的接收
*
* 确定自己的端口号,10001.绑定:将自己的程序和端口绑定。程序监听10001端口
*
*/
public class TestUDPReceive {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
System.out.println("接收端启动:");
//[1]创建UDP服务,绑定端口
DatagramSocket ds=new DatagramSocket(10001);//接收方,带参数,绑定一个端口
//[2]定义数据包
byte[]buf=new byte[64*1024];
DatagramPacket dp=new DatagramPacket(buf, buf.length);//接收的数据包,2个参数
//[3]接收
ds.receive(dp);//
//[4]拆包
String data=new String(dp.getData(),0,dp.getLength());
String address=dp.getAddress().getHostName();//发送数据的机器名
System.out.println("接收到"+address+"发送的数据:"+data);
}
}
我们已经会用UDP协议发送字符串了,当然UDP还可以发送图片
说明:
客户端
:发送图片和发送字符串的步骤是一模一样的,只需要更改发送的内容即可(一定要确保自己有图片哦),还要考虑文件不存在时要处理异常
服务器
:接收的步骤也是一模一样,服务器要一直运行,不能终止,所以要写一个死循环,还有路径不存在时如何创建路径
UDP图片传输客户端代码:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class UDPClient {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
System.out.println("发送端启动:");
// 数据,文件的数据读出来--->字节数组
String dir = "D:\\tu\\pic";
try {
// 输入文件名
Scanner input = new Scanner(System.in);
System.out.println("请输入要上传的文件名:");
String fileName = input.next();
File file = new File(dir, fileName);
// 字节输入流,和文件关联
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[64 * 1024];
int len = fis.read(buf);// 要发送的数据已经读入到buf
// [1]建立UDP的服务
DatagramSocket ds = new DatagramSocket();// 发送方不用参数
// [2]打包
InetAddress ip = InetAddress.getByName("192.168.56.1");// 服务器的地址
int port = 9000;// 服务器的程序端口号
DatagramPacket dp = new DatagramPacket(buf, len, ip, port);// 发送的数据包,4个参数
// [3]发送
ds.send(dp);
// [4]关闭
ds.close();
System.out.println("发送结束");
} catch (FileNotFoundException e) {
// TODO: handle exception
System.out.println("文件不存在,重新输入");
}
}
}
UDP图片传输服务端代码:
import java.io.File;
import java.io.FileOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/*
* 服务器端
*
* 绑定端口号:9000
*
* 接收数据,----写入文件,文件保存路径“D:\tu\pic1”
*
*
*/
public class UDPServer {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
System.out.println("接收端启动:");
// [1]创建UDP服务,绑定端口
DatagramSocket ds = new DatagramSocket(9000);// 接收方,带参数,绑定一个端口
// 将收到的字节数组数据,写入到文件
String dir = "D:\\tu\\pic1";
// 确定路径存在,不存在就创建
File path = new File(dir);
if (!path.exists()) {
path.mkdir();// 创建目录
}
int count = 0;
while (true) {
// [2]定义数据包
byte[] buf = new byte[64 * 1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);// 接收的数据包,2个参数
// [3]接收
ds.receive(dp);//
// [4]拆包。
File file = new File(dir, "receive" + (count++) + ".jpg");
// 字节输出流
FileOutputStream fos = new FileOutputStream(file);
// 数据写入到文件
fos.write(dp.getData(), 0, dp.getLength());
fos.close();
String address = dp.getAddress().getHostName();// 发送数据的机器名
System.out.println("接收到" + address + "发送的数据 ");
}
}
}
TCP协议:
- 定义:TCP协议是面向连接的通信协议,即在传输数据前先在发送端和接收端建
立逻辑连接,然后再传输数据,保证了两台计算机之间可靠无差错的数据传输
说明:- TCP协议传送速度较慢,但传送的数据比较可靠。
- 由于TCP协议的面向连接特性,它可以保证传输数据的安全性和完整性,所以
是一个被广泛采用的协议,例如在下载文件时,如果数据接收不完整,将会导
致文件数据丢失而不能被打开,因此,下载文件时必须采用TCP协议。
案例1(TCP实现登录的操作):
- 1️⃣数据库建表(存储用户名和密码)
- 2️⃣连接数据库
- 3️⃣创建用户实体类(封装用户名和密码)
- 4️⃣发送信息到服务器(转换为json字符串传输)
- 5️⃣服务器验证信息,返回客户端
- 6️⃣展示结果
这里如果服务器要是用单线程的话就有弊端,在一个客户端接入的时候,其他的客户端就不能再接入,这样就大大的浪费了时间和资源,所以我们要在服务器开启多线程,每接入一个客户端就开启一个线程
连接数据库代码:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JDBCUtils {
public static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/要连接的数据库名称?characterEncoding=UTF-8";
public static final String USERNAME = "root";
public static final String PASSWORD = "密码";//安装mySQL设置的密码
public static Connection conn=null ;
//使用静态代码块给静态常量成员赋初值
static{
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection(){
return conn;
}
}
用户实体类代码:
import java.io.Serializable;
/*
* 用户的实体类
*/
public class UserBean implements Serializable {
private static final long serialVersionUID = 1L;
private String name;//用户名
private String password;//密码
//构造方法,带参数
public UserBean(String name, String password) {
this.name = name;
this.password = password;
}
//set和get
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name+"\t---\t"+password;
}
}
TCP传输客户端代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
import com.google.gson.Gson;
/*
* 客户端
*
* 连接服务器
*
* 发送一个字符串hello
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
System.out.println("客户端启动");
//[1]创建tcp服务,客户端Socket
InetAddress ip=InetAddress.getLocalHost();//服务器地址,本机
int port=10010;//服务器程序的端口
Socket client=new Socket(ip,port);
//传输数据
System.out.println("开始传输数据");
Scanner scanner=new Scanner(System.in);
System.out.println("用户名:");
String name=scanner.next();
System.out.println("密码:");
String password=scanner.next();
//封装成对象
UserBean user=new UserBean(name, password);
//将对象发送给服务器
//将对象转换成json字符串,发送
Gson gson=new Gson();
String data=gson.toJson(user);//将user对象转换成字符串
System.out.println("json字符串:"+data);
//通过连接获取io流,用输出流将数据传输出去
//字节流----网络传输,数据都是字节
OutputStream os=client.getOutputStream();
os.write(data.getBytes());
os.flush();
System.out.println("传输结束");
//接收验证结果
//结果是一个字符串---接收字节数组
//流,输入流--client服务封装
InputStream in=client.getInputStream();
byte[]buf=new byte[1024];
int len;//接收的字节个数
len=in.read(buf);
String receiveData=new String(buf,0,len);
//接收到结果,根据结果执行不同功能
System.out.println("验证结果:"+receiveData);
client.close();
}
}
TCP传输服务器代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.google.gson.Gson;
/*
* 服务器端
*
* 绑定端口10010
*
* 接收客户端的数据(字符串)
*/
class MyRunable implements Runnable {
Socket client;// 定义变量,保存接收的数据
// 构造方法的参数来接收数据
public MyRunable(Socket s) {
// TODO Auto-generated constructor stub
client = s;
}
@Override
public void run() {
// 线程需要从客户端获取数据,验证,将验证结果发送给客户端
// Socket对象,封装io流
// TODO Auto-generated method stub
// [3]获取客户端的数据
// io流,和客户端和服务器的连接服务中封装
InputStream is;
try {
is = client.getInputStream();
byte[] buf = new byte[1024];
int len = is.read(buf);
String data = new String(buf, 0, len);// 客户端发过来的json字符串
// 将json字符串转换成UserBean对象
Gson gson = new Gson();
UserBean user = gson.fromJson(data, UserBean.class);// 第一个参数要转换的字符串,第二个参数是转换的类型
String ip = client.getInetAddress().getHostAddress();
System.out.println("接收到 " + ip + " 数据:" + user.toString());
// 验证
// 到用户表查找name=user.name并且password=user.password的记录
// 连接数据库
Connection conn = JDBCUtils.getConnection();
// sql语句
String sql = "SELECT * FROM user_info WHERE NAME=? AND PASSWORD=?";
PreparedStatement pStatement = conn.prepareStatement(sql);
pStatement.setString(1, user.getName());
pStatement.setString(2, user.getPassword());
ResultSet rs = pStatement.executeQuery();
// 有这条记录---,验证通过
// 没有这条记录---,验证不通过
String result = "";// 验证结果
if (rs.next()) {
result = "pass";
} else {
result = "not match";
}
System.out.println("验证结果:" + result);
// 服务器将验证结果发给客户端
// 发送的数据String
// 使用io流,字节输出流---封装到client服务中
OutputStream out = client.getOutputStream();
out.write(result.getBytes());
out.flush();
System.out.println("将验证结果发送给客户端");
client.close();
} catch (IOException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class TCPServer {
public static void main(String[] args) throws IOException,
ClassNotFoundException, SQLException {
// TODO Auto-generated method stub
System.out.println("服务器启动");
// [1]创建服务,服务器端Serversocket
int port = 10010;
ServerSocket server = new ServerSocket(port);
while (true) {
// [2]接入客户端
Socket client = server.accept();// 阻塞式的方法,没有客户端接入,就会一直等待
System.out.println("接入一个客户端");
//创建线程
MyRunable r = new MyRunable(client);
Thread serverThread = new Thread(r);
//开启线程
serverThread.start();
}
}
}
案例2(TCP实现资源的下载):
- 1️⃣数据库建表(存储资源的路径)
- 2️⃣连接数据库
- 3️⃣发送要下载资源的编号到服务器
- 4️⃣接收编号,查找资源
- 5️⃣将资源发送给客户端
- 6️⃣接收并下载资源
这里我们同样要开启多线程
连接数据库代码(和之前一模一样只需跟换数据库的名称即可):
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class TCPUtils {
public static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/要连接的数据库名?characterEncoding=UTF-8";
public static final String USERNAME = "root";
public static final String PASSWORD = "密码";//安装mySQL设置的密码
public static Connection conn=null ;
//使用静态代码块给静态常量成员赋初值
static{
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection(){
return conn;
}
}
TCP资源下载客户端代码:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
public class DownloadClient {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
System.out.println("客户端启动");
//连接服务器
int port=10010;
//服务器的ip,本机
InetAddress ip=InetAddress.getLocalHost();
Socket client=new Socket(ip, port);
//向服务器发送资源编号
//编号---键盘输入
String no;//编号
System.out.println("输入下载资源的编号:");
Scanner scanner=new Scanner(System.in);
no=scanner.next();
//no编号发送给服务器
OutputStream out=client.getOutputStream();
out.write(no.getBytes());
System.out.println("下载服务器发送来的资源");
//读输入流的数据--->字节数组,封装
InputStream in=client.getInputStream();
//字节数组---->保存到文件,
File file=new File("D:/client/receive/receive1.mp3");
FileOutputStream fos=new FileOutputStream(file);
byte[]buf=new byte[1024*10];
int len;
while((len=in.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
client.close();
System.out.println("下载完成");
}
}
TCP资源下载服务器代码:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.*;
class Runable implements Runnable {
Socket client;// 定义变量,保存接收的数据
// 构造方法的参数来接收数据
public Runable(Socket s) {
// TODO Auto-generated constructor stub
client = s;
}
@Override
public void run() {
// 线程需要从客户端获取数据,验证,将验证结果发送给客户端
// Socket对象,封装io流
// TODO Auto-generated method stub
// [3]获取客户端的数据
// io流,和客户端和服务器的连接服务中封装
InputStream in;
try {
in = client.getInputStream();
//接收一个资源编号
String no;//编号
byte[] buf = new byte[1024];
int len = in.read(buf);//将流中的数据读入到buf数组
no = new String(buf, 0, len);
System.out.println("接收到编号:" + no);
//获取输入流
// InputStream in = client.getInputStream();
//给资源文件编号
//编号-文件
//集合/存储在数据库
// 连接数据库
Connection conn = TCPUtils.getConnection();
//获取数据库操作对象
Statement stmt = conn.createStatement();
// sql语句
String sql = "select * from resource";
ResultSet rs = stmt.executeQuery(sql);
//根据编号查找资源文件
while (rs.next()) {
if (rs.getString("number").equals(no)) {
String path = rs.getString("address");
//有这个资源文件,发送
//封装文件
File file = new File(path);
//输入流读文件的数据--->输出流发送给客户端
//输入流 源文件---->字节数组
//字节数组--->输出流 客户端
FileInputStream fis = new FileInputStream(file);
OutputStream out = client.getOutputStream();
byte[] fbuf = new byte[1024 * 10];
int flen;
while ((flen = fis.read(fbuf)) != -1) {
//将读入的数据写入到输出流
out.write(fbuf, 0, flen);
out.flush();
}
//传输结束
fis.close();//关闭输入流
//通知客户端,传输结束了
client.shutdownInput();
client.close();
}
}
} catch (IOException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class DownloadServer {
public static void main(String[] args) throws IOException, SQLException {
// TODO Auto-generated method stub
//声明自己的端口
int port = 10010;
System.out.println("服务器启动");
//服务器的ip,本机
InetAddress ip = InetAddress.getLocalHost();
ServerSocket server = new ServerSocket(port);
//接收
while (true) {
// [2]接入客户端
Socket client = server.accept();// 阻塞式的方法,没有客户端接入,就会一直等待
System.out.println("接入一个客户端");
// 创建线程
Runable r = new Runable(client);
Thread serverThread = new Thread(r);
// 开启线程
serverThread.start();
}
}
}