👨💻更多精彩尽在博主首页:i新木优子👀
🏆Java基础系列教程:传送门💦
🧚♂️寄语:不经历风雨,长不成大树,不受百炼,难以成钢👣
🎉欢迎关注🔍点赞👍收藏⭐留言📝
🙏作者水平有限,发现错误欢迎留言轰炸
网络编程就是要实现在网络中不同计算机之间的通信
如何实现不同计算机之间的数据传输呢?
计算机之间的传输就和我们现实中的收发快递是一样的,首先就要知道收件人的地址,在网络传输中就是目标计算机的地址也就是常说的IP地址(唯一且不重复的),快递送到地址之后还要正确的送到收件人手中,对应的就计算机中的某一个应用程序也就是端口号(port),在快递运送的时候我们可以选择不同的快递公司,在网络中就是网络通信协议。网络传输三要素:
IP地址
:唯一标识网络上的每一台计算机,两台计算机之间通信的必备要素。端口号
:计算机中应用的标号(代表一个应用程序),0-1024系统使用或保留端口,端口号占2个字节,所以有效端口0-65535.通信协议
:通信的规则TCP,UDP
网络通信协议:
- 定义:通过计算机网络可以使多台计算机实现连接,但是位于同一个网络中的计算机在进行连接和通信时必须要遵守一定的规则。在计算机网络中,这些连接和通信的规则被称为网络通信协议。 作用:它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交互。
- 分类:目前应用最广泛的有TCP/IP协议(Transmission Control Protocol/Internet Protocol,传输控制协议/英特网互联协议)、UDP协议(User Datagram Protocol,用户数据报协议)和其他一些协议的协议组。
网络通信协议一TCP/IP网络模型:
查看自己计算机的IP地址
按win+R
打开命令提示符输入:
ipconfig/all
UDP协议:
定义:UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接 说明:
使用UDP协议消耗资源小、通信效率高、延迟小,所以通常都会用于音频、视 频和普通数据的传输,例如视频会议都使用UDP协议。
使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整 性,因此在传输重要数据时不建议使用UDP协议。
UDP传输时数据包大小不能超过64K
UDP传输发送方的步骤:1️⃣建立UDP的服务
2️⃣打包
3️⃣发送
4️⃣关闭
UDP接收方的步骤:
- 1️⃣创建UDP服务,绑定端口
- 2️⃣定义数据包
- 3️⃣接收
- 4️⃣拆包
Java在网络传输中自己就封装好了类,直接调用即可,不理解的小伙伴可以自己查看一下api文档哦
🦄UDP发送端代码:
importjava.net.DatagramPacket;importjava.net.DatagramSocket;importjava.net.InetAddress;importjava.net.SocketException;/*
* UDP的发送
*
* 将“hello i新木优子”发送给计算机“192.168.56.1”的10001端口的程序
*/publicclassTestUDPSend{publicstaticvoidmain(String[] args)throwsException{System.out.println("发送端启动:");//[1]建立UDP的服务DatagramSocket ds=newDatagramSocket();//发送方不用参数//[2]打包String data="hello i新木优子";byte[]buf=data.getBytes();InetAddress ip=InetAddress.getByName("192.168.56.1");//IP地址int port=10001;//端口号DatagramPacket dp=newDatagramPacket(buf, buf.length, ip, port);//发送的数据包,4个参数//[3]发送
ds.send(dp);//[4]关闭
ds.close();System.out.println("发送结束");}}
🦄UDP接收端代码:
importjava.io.IOException;importjava.net.DatagramPacket;importjava.net.DatagramSocket;importjava.net.SocketException;/*
* UDP的接收
*
* 确定自己的端口号,10001.绑定:将自己的程序和端口绑定。程序监听10001端口
*
*/publicclassTestUDPReceive{publicstaticvoidmain(String[] args)throwsIOException{// TODO Auto-generated method stubSystem.out.println("接收端启动:");//[1]创建UDP服务,绑定端口DatagramSocket ds=newDatagramSocket(10001);//接收方,带参数,绑定一个端口//[2]定义数据包byte[]buf=newbyte[64*1024];DatagramPacket dp=newDatagramPacket(buf, buf.length);//接收的数据包,2个参数//[3]接收
ds.receive(dp);////[4]拆包String data=newString(dp.getData(),0,dp.getLength());String address=dp.getAddress().getHostName();//发送数据的机器名System.out.println("接收到"+address+"发送的数据:"+data);}}
在运行的时候一定要先启动接收端,再启动发送方
运行结果:
发送端:
接收端:
我们已经会用UDP协议发送字符串了,当然UDP还可以发送图片
说明:客户端
:发送图片和发送字符串的步骤是一模一样的,只需要更改发送的内容即可(一定要确保自己有图片哦),还要考虑文件不存在时要处理异常
服务器
:接收的步骤也是一模一样,服务器要一直运行,不能终止,所以要写一个死循环,还有路径不存在时如何创建路径
🦅UDP图片传输客户端代码:
importjava.io.File;importjava.io.FileInputStream;importjava.io.FileNotFoundException;importjava.io.IOException;importjava.net.DatagramPacket;importjava.net.DatagramSocket;importjava.net.InetAddress;importjava.util.Scanner;publicclassUDPClient{publicstaticvoidmain(String[] args)throwsIOException{// TODO Auto-generated method stubSystem.out.println("发送端启动:");// 数据,文件的数据读出来--->字节数组String dir ="D:\\tu\\pic";try{// 输入文件名Scanner input =newScanner(System.in);System.out.println("请输入要上传的文件名:");String fileName = input.next();File file =newFile(dir, fileName);// 字节输入流,和文件关联FileInputStream fis =newFileInputStream(file);byte[] buf =newbyte[64*1024];int len = fis.read(buf);// 要发送的数据已经读入到buf// [1]建立UDP的服务DatagramSocket ds =newDatagramSocket();// 发送方不用参数// [2]打包InetAddress ip =InetAddress.getByName("192.168.56.1");// 服务器的地址int port =9000;// 服务器的程序端口号DatagramPacket dp =newDatagramPacket(buf, len, ip, port);// 发送的数据包,4个参数// [3]发送
ds.send(dp);// [4]关闭
ds.close();System.out.println("发送结束");}catch(FileNotFoundException e){// TODO: handle exceptionSystem.out.println("文件不存在,重新输入");}}}
🦅UDP图片传输服务端代码:
importjava.io.File;importjava.io.FileOutputStream;importjava.net.DatagramPacket;importjava.net.DatagramSocket;/*
* 服务器端
*
* 绑定端口号:9000
*
* 接收数据,----写入文件,文件保存路径“D:\tu\pic1”
*
*
*/publicclassUDPServer{publicstaticvoidmain(String[] args)throwsException{// TODO Auto-generated method stubSystem.out.println("接收端启动:");// [1]创建UDP服务,绑定端口DatagramSocket ds =newDatagramSocket(9000);// 接收方,带参数,绑定一个端口// 将收到的字节数组数据,写入到文件String dir ="D:\\tu\\pic1";// 确定路径存在,不存在就创建File path =newFile(dir);if(!path.exists()){
path.mkdir();// 创建目录}int count =0;while(true){// [2]定义数据包byte[] buf =newbyte[64*1024];DatagramPacket dp =newDatagramPacket(buf, buf.length);// 接收的数据包,2个参数// [3]接收
ds.receive(dp);//// [4]拆包。File file =newFile(dir,"receive"+(count++)+".jpg");// 字节输出流FileOutputStream fos =newFileOutputStream(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️⃣展示结果
这里如果服务器要是用单线程的话就有弊端,在一个客户端接入的时候,其他的客户端就不能再接入,这样就大大的浪费了时间和资源,所以我们要在服务器开启多线程,每接入一个客户端就开启一个线程
🐍连接数据库代码:
importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.SQLException;publicclassJDBCUtils{publicstaticfinalString DRIVER_CLASS_NAME ="com.mysql.jdbc.Driver";publicstaticfinalString URL ="jdbc:mysql://localhost:3306/要连接的数据库名称?characterEncoding=UTF-8";publicstaticfinalString USERNAME ="root";publicstaticfinalString PASSWORD ="密码";//安装mySQL设置的密码publicstaticConnection 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();}}publicstaticConnectiongetConnection(){return conn;}}
🐍用户实体类代码:
importjava.io.Serializable;/*
* 用户的实体类
*/publicclassUserBeanimplementsSerializable{privatestaticfinallong serialVersionUID =1L;privateString name;//用户名privateString password;//密码//构造方法,带参数publicUserBean(String name,String password){this.name = name;this.password = password;}//set和getpublicStringgetName(){return name;}publicvoidsetName(String name){this.name = name;}publicStringgetPassword(){return password;}publicvoidsetPassword(String password){this.password = password;}@OverridepublicStringtoString(){// TODO Auto-generated method stubreturn name+"\t---\t"+password;}}
🐍TCP传输客户端代码:
importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.InetAddress;importjava.net.Socket;importjava.util.Scanner;importcom.google.gson.Gson;/*
* 客户端
*
* 连接服务器
*
* 发送一个字符串hello
*/publicclassTCPClient{publicstaticvoidmain(String[] args)throwsIOException{// TODO Auto-generated method stubSystem.out.println("客户端启动");//[1]创建tcp服务,客户端SocketInetAddress ip=InetAddress.getLocalHost();//服务器地址,本机int port=10010;//服务器程序的端口Socket client=newSocket(ip,port);//传输数据System.out.println("开始传输数据");Scanner scanner=newScanner(System.in);System.out.println("用户名:");String name=scanner.next();System.out.println("密码:");String password=scanner.next();//封装成对象UserBean user=newUserBean(name, password);//将对象发送给服务器//将对象转换成json字符串,发送Gson gson=newGson();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=newbyte[1024];int len;//接收的字节个数
len=in.read(buf);String receiveData=newString(buf,0,len);//接收到结果,根据结果执行不同功能System.out.println("验证结果:"+receiveData);
client.close();}}
🐍TCP传输服务器代码:
importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.ServerSocket;importjava.net.Socket;importjava.sql.Connection;importjava.sql.PreparedStatement;importjava.sql.ResultSet;importjava.sql.SQLException;importcom.google.gson.Gson;/*
* 服务器端
*
* 绑定端口10010
*
* 接收客户端的数据(字符串)
*/classMyRunableimplementsRunnable{Socket client;// 定义变量,保存接收的数据// 构造方法的参数来接收数据publicMyRunable(Socket s){// TODO Auto-generated constructor stub
client = s;}@Overridepublicvoidrun(){// 线程需要从客户端获取数据,验证,将验证结果发送给客户端// Socket对象,封装io流// TODO Auto-generated method stub// [3]获取客户端的数据// io流,和客户端和服务器的连接服务中封装InputStream is;try{
is = client.getInputStream();byte[] buf =newbyte[1024];int len = is.read(buf);String data =newString(buf,0, len);// 客户端发过来的json字符串// 将json字符串转换成UserBean对象Gson gson =newGson();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();}}}publicclassTCPServer{publicstaticvoidmain(String[] args)throwsIOException,ClassNotFoundException,SQLException{// TODO Auto-generated method stubSystem.out.println("服务器启动");// [1]创建服务,服务器端Serversocketint port =10010;ServerSocket server =newServerSocket(port);while(true){// [2]接入客户端Socket client = server.accept();// 阻塞式的方法,没有客户端接入,就会一直等待System.out.println("接入一个客户端");//创建线程MyRunable r =newMyRunable(client);Thread serverThread =newThread(r);//开启线程
serverThread.start();}}}
运行结果:
数据库表:客户端:
服务器:
案例2(TCP实现资源的下载):
- 1️⃣数据库建表(存储资源的路径)
- 2️⃣连接数据库
- 3️⃣发送要下载资源的编号到服务器
- 4️⃣接收编号,查找资源
- 5️⃣将资源发送给客户端
- 6️⃣接收并下载资源
这里我们同样要开启多线程
🐎连接数据库代码(和之前一模一样只需跟换数据库的名称即可):
importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.SQLException;publicclassTCPUtils{publicstaticfinalString DRIVER_CLASS_NAME ="com.mysql.jdbc.Driver";publicstaticfinalString URL ="jdbc:mysql://localhost:3306/要连接的数据库名?characterEncoding=UTF-8";publicstaticfinalString USERNAME ="root";publicstaticfinalString PASSWORD ="密码";//安装mySQL设置的密码publicstaticConnection 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();}}publicstaticConnectiongetConnection(){return conn;}}
🐎TCP资源下载客户端代码:
importjava.io.File;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.InetAddress;importjava.net.Socket;importjava.util.Scanner;publicclassDownloadClient{publicstaticvoidmain(String[] args)throwsIOException{// TODO Auto-generated method stubSystem.out.println("客户端启动");//连接服务器int port=10010;//服务器的ip,本机InetAddress ip=InetAddress.getLocalHost();Socket client=newSocket(ip, port);//向服务器发送资源编号//编号---键盘输入String no;//编号System.out.println("输入下载资源的编号:");Scanner scanner=newScanner(System.in);
no=scanner.next();//no编号发送给服务器OutputStream out=client.getOutputStream();
out.write(no.getBytes());System.out.println("下载服务器发送来的资源");//读输入流的数据--->字节数组,封装InputStream in=client.getInputStream();//字节数组---->保存到文件,File file=newFile("D:/client/receive/receive1.mp3");FileOutputStream fos=newFileOutputStream(file);byte[]buf=newbyte[1024*10];int len;while((len=in.read(buf))!=-1){
fos.write(buf,0,len);}
fos.close();
client.close();System.out.println("下载完成");}}
🐎TCP资源下载服务器代码:
importjava.io.File;importjava.io.FileInputStream;importjava.io.IOException;importjava.io.InputStream;importjava.io.OutputStream;importjava.net.InetAddress;importjava.net.ServerSocket;importjava.net.Socket;importjava.sql.*;classRunableimplementsRunnable{Socket client;// 定义变量,保存接收的数据// 构造方法的参数来接收数据publicRunable(Socket s){// TODO Auto-generated constructor stub
client = s;}@Overridepublicvoidrun(){// 线程需要从客户端获取数据,验证,将验证结果发送给客户端// Socket对象,封装io流// TODO Auto-generated method stub// [3]获取客户端的数据// io流,和客户端和服务器的连接服务中封装InputStream in;try{
in = client.getInputStream();//接收一个资源编号String no;//编号byte[] buf =newbyte[1024];int len = in.read(buf);//将流中的数据读入到buf数组
no =newString(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 =newFile(path);//输入流读文件的数据--->输出流发送给客户端//输入流 源文件---->字节数组//字节数组--->输出流 客户端FileInputStream fis =newFileInputStream(file);OutputStream out = client.getOutputStream();byte[] fbuf =newbyte[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();}}}publicclassDownloadServer{publicstaticvoidmain(String[] args)throwsIOException,SQLException{// TODO Auto-generated method stub//声明自己的端口int port =10010;System.out.println("服务器启动");//服务器的ip,本机InetAddress ip =InetAddress.getLocalHost();ServerSocket server =newServerSocket(port);//接收while(true){// [2]接入客户端Socket client = server.accept();// 阻塞式的方法,没有客户端接入,就会一直等待System.out.println("接入一个客户端");// 创建线程Runable r =newRunable(client);Thread serverThread =newThread(r);// 开启线程
serverThread.start();}}}
运行结果:
数据库表:客户端:
服务器:
服务器中的资源:下载的资源:
版权归原作者 i新木优子 所有, 如有侵权,请联系我们删除。