一、网络编程(了解)
1.1 网络编程的基本知识
1、IP
2、端口号
3、网络协议
1.2 Socket
无论是发送端还是接收端,无论是TCP还是UDP,都必须有Socket对象。Socket对象是Java中负责与底层的网卡驱动交互的对象。
- UDP协议:DatagramSocket类
- TCP协议:ServerSocket类和Socket类
1.3 基于UDP协议进行上层开发
3.3.1 发送端
基于UDP协议的发送端,在发送数据之前不会检查对方在不在,能不能成功传输数据,只管发送。
package com.mytest.net;
import java.net.*;
public class Send {//发送端
public static void main(String[] args) throws Exception {
//(1)创建一个DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
//(2)准备要发送的消息
String str = "马上要吃饭了,大家吃什么?";
//(3)把数据打包,打包成 数据报格式
//需要创建DatagramPacket对象
byte[] data = str.getBytes();//把字符串转为字节输出,才能在网络中传输
// InetAddress ip = InetAddress.getByName("www.123vwu.com");//基于域名的方式
byte[] address = {(byte)192,(byte)168,36,21};
/*
192的二进制:
int类型:00000000 00000000 00000000 11000000
byte类型:11000000 IP地址是没有负数的,因为底层最高位仍然是数据位,不是符号位
因为底层又把这个byte转为int
*/
InetAddress ip = InetAddress.getByAddress(address);//基于IP地址的方式
int port = 8888;//双方约定
DatagramPacket dp = new DatagramPacket(data,0,data.length,ip,8888);
//(4)发送
//通过socket发送
//数据流向: 程序中byte[]数组 -> dp包裹 -> ds(socket对象) -> 接收端
ds.send(dp);
//(5)关闭
ds.close();
}
}
3.3.2 接收端
先运行接收端,才能确保消息能收到。
package com.mytest.net;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Receiver {//接收端
public static void main(String[] args) throws Exception{
//(1)创建一个DatagramSocket对象
//告诉网卡驱动,网卡驱动程序需要在8888监听传给Receiver程序的数据
DatagramSocket ds = new DatagramSocket(8888);
//(2)准备一个字节数组,用来装接收到的数据
byte[] data = new byte[1024];
//(3)准备一个DatagramPacket对象,用于接收对方的数据报报文等信息
//这里没有写IP地址,因为不用限制谁给我发,都可以给我发,只要对方发的时候,写明我的IP地址和端口号是8888就可以
DatagramPacket dp = new DatagramPacket(data,0,data.length);
//(4)接收
ds.receive(dp);//如果没有人给你发消息,这句代码会阻塞等待
//(5)查看消息
int len = dp.getLength();//实际接收了几个字节
System.out.println(new String(data,0,len));
//(6)断开
ds.close();
}
}
3.4 基于TCP协议进行上层开发
基于TCP协议的网络应用程序必须分为服务器端和客户端。是C/S结构,C是Client,S是Server。
3.4.1 案例
需求:
- 服务器开启,监听和等待客户端的连接
- 客户端连接服务器
- 服务器监听到客户端连接之后,给客户端发一句话:欢迎登录
- 客户端接收服务器发送的消息
1、服务器端
package com.mytest.tcp1;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class Server1 {
public static void main(String[] args) throws Exception{
//(1)创建ServerSocket对象,用于监听和等待客户端的连接,它不负责传输数据。
ServerSocket server = new ServerSocket(8888);
Socket socket = server.accept();//如果没有客户端连接,这句代码会阻塞
InetAddress clientIP = socket.getInetAddress();
System.out.println(clientIP.getHostAddress() +"连接服务器成功!");
//(2)准备发送的消息
String str = "欢迎登录";
byte[] data = str.getBytes();//如果没有指定具体的编码方式,就按照IDEA运行环境的编码处理UTF-8
//(3)TCP协议要传输数据,需要用到字节流
OutputStream out = socket.getOutputStream();
//数据的流向: 服务器端程序 data数组-> out输出流 -> socket -> 网络 -> 客户端程序
out.write(data);//输出整个字节数组
//(4)关闭
out.close();
socket.close();
server.close();
}
}
2、客户端
package com.mytest.tcp1;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
public class Client1 {
public static void main(String[] args) throws Exception{
//(1)创建一个Socket对象,用于连接服务器
//必须指定服务器的IP地址和端口号
byte[] address = {(byte)192,(byte)168,36,21};
InetAddress ip = InetAddress.getByAddress(address);//基于IP地址的方式
int port = 8888;//双方约定
Socket socket = new Socket(ip,port);
//(2)接收服务器端的消息,TCP协议要传输数据,需要用到字节流
InputStream in = socket.getInputStream();
byte[] data = new byte[1024];
while(true){
int len = in.read(data);
if(len == -1){
break;
}
System.out.println(new String(data,0,len));
}
//(3)关闭
in.close();
socket.close();
}
}
3.4.4 案例
需求:
服务器:
- 监听和等待客户端的连接
- 接收客户端上传的文件,保存到服务器指定目录下,例如:服务器端D:\upload文件夹下
- 接收完文件之后给客户端反馈:xxx文件上传成功!如果中途出现异常,反馈:xxx文件上传失败!
客户端:
-
从键盘输入要上传的本地文件的完整路径值
-
将指定的文件上传到服务器
-
接收上传结果
-
可以多个客户端同时上传
1、服务器端
package com.mytest.tcp4;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server4 {
public static void main(String[] args)throws Exception {
//(1)创建ServerSocket的对象,监听和接收客户端的连接
ServerSocket server = new ServerSocket(8888);
while(true) {
Socket socket = server.accept();//它只要执行了,就代表一个客户端连接成功了
UploadThread thread =new UploadThread(socket);
thread.start();
}
}
private static class UploadThread extends Thread{
private Socket socket;
public UploadThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try(
//接收(1)文件名.扩展名(2)文件内容
InputStream in = socket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(in);//提高效率
ObjectInputStream ois = new ObjectInputStream(bis);
OutputStream out = socket.getOutputStream();
PrintStream ps = new PrintStream(out);
){
//先接收(1)文件名.扩展名
String filename = ois.readUTF();
String clientIP = socket.getInetAddress().getHostAddress();
String newFilename = System.currentTimeMillis() + "_" + clientIP + "_" + filename;
//再接收(2)文件内容
//数据的流向:客户端 -> in -> bis -> ois -> data -> bos->fos-> 服务器本地的d:\\upload文件夹下
boolean flag = true;
try(FileOutputStream fos = new FileOutputStream("d:\\upload\\"+newFilename);
BufferedOutputStream bos = new BufferedOutputStream(fos);//提高效率
) {
byte[] data = new byte[1024];
while (true) {
int len = ois.read(data);//从客户端接收文件内容
if (len == -1) {
break;
}
bos.write(data, 0, len);//写到服务器的本地文件中
}
}catch (Exception e){
flag = false;
}
ps.println(flag ? filename+"文件上传成功!" : filename+"文件上传失败!");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
2、客户端
package com.mytest.tcp4;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Client4 {
public static void main(String[] args) throws Exception{
//(1)创建Socket对象,因为只有他才可以建立连接,与网卡驱动交互,进行数据的传输
//这里填写的是服务器端的IP地址和端口号。
Socket socket = new Socket("192.168.36.21",8888);
//(2)键盘输入完整的文件路径值
Scanner keyboard = new Scanner(System.in);
System.out.print("请输入你要上传的文件的完整路径值:");
String filepath = keyboard.nextLine();//因为可能目录或文件名中包含空格
//D:\temp\img\dog.jpg
File file = new File(filepath);
//(3)把这个文件上传给服务器
OutputStream out = socket.getOutputStream();
//分别上传(1)文件名.后缀名 和 (2)文件内容
//为了服务器端方便区分是(1)文件名.后缀名 和 (2)文件内容,这里用ObjectOutputStream来传输数据
BufferedOutputStream bos = new BufferedOutputStream(out);//提高效率
ObjectOutputStream oos = new ObjectOutputStream(bos);
String filename = file.getName();//得到文件名
oos.writeUTF(filename);//对方用 ObjectInputStream流的readUTF()读取
//从本地读取文件内容,再发送给服务器
FileInputStream fis = new FileInputStream(filepath);
BufferedInputStream bis = new BufferedInputStream(fis);//提高效率
byte[] data = new byte[1024];
//数据流向: filepath对应的本地文件 -> fis -> bis -> data数组 -> oos -> bos -> out -> 网络中 -> 服务器
while(true){
int len = bis.read(data);//从本地文件读取内容
if(len==-1){
break;
}
oos.write(data,0,len);//发送给服务器
}
oos.flush();
socket.shutdownOutput();//关闭输出通道,但是连接保留
// socket.close(); 或 oos.close(); //会导致连接断开
//接收服务器反馈的结果,约定按行读取
InputStream in = socket.getInputStream();
Scanner netInput = new Scanner(in);
while(netInput.hasNextLine()){
String result = netInput.nextLine();
System.out.println("结果:" + result);
}
netInput.close();
in.close();
bis.close();
fis.close();
oos.close();
bos.close();
out.close();
keyboard.close();
socket.close();
}
}