网络编程的三要素
ip地址:网络中某一台主机的唯一标识
ipV4: 32位组成,采用点分十进制表示法: 192.168.1.66
ipV6: 128位组成,采用冒分十六进制表示法: fe80::a0ca:ef4a:766c:62d3%13
ip的命令
ipconfig: 查看本机的ip地址
ping 域名/ip地址: 查看网络是否连通
端口号:主机上连接网络的应用程序的一个标识(0~65535)
通信协议:数据传输的规则
UDP:面向无连接的不可靠的协议
TCP:面向有链接可靠的协议
InetAddress类
在Java语言中使用InetAddress类表示IP地址对象,可以用来获取主机名,主机地址等信息。
public static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址。
public String getHostName()
获取主机名
public String getHostAddress()
获取主机ip地址
//获取InetAddress对象
InetAddress address = InetAddress.getByName("127.0.0.1");
//获取主机名
String hostName = address.getHostName();
//获取主机地址
String hostAddress = address.getHostAddress();
UDP发送和接收
发送端
//1.创建DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
//2.创建数据包DatagramPacket对象
byte[] bytes = "你好啊".getBytes(); //需要发送的数据
int length = bytes.length; //数据的长度
InetAddress address = InetAddress.getByName("127.0.0.1"); //对方的ip地址
int port =10086; //对方的端口号
//把前面准备的好数据,以及ip地址,端口号 封装为对象
DatagramPacket dp = new DatagramPacket(bytes,length,address,port);
//3.发送数据包
ds.send(dp);
//4.释放资源
ds.close();
接收端
//1.创建DatagramSocket对象
DatagramSocket ds = new DatagramSocket(10086);
//2.创建数据包DatagramPacet对象
byte[] bs = new byte[1024];
DatagramPacket dp = new DatagramPacket(bs,bs.length);
//3.接收数据,数据会存储到DatagramPacet数据包中
ds.receive(dp);
//4.拆包,获取数据等信息
byte[] data = dp.getData(); //获取字节数组
int length = dp.getLength(); //获取有效的字节个数
InetAddress address = dp.getAddress(); //获取发送端的ip
//把字节数组中有效的部分转换为字符串
String str=new String(data,0,length);
System.out.println(address+"-->"+ str);
//5.释放资源
ds.close();
广播和组播
- 组播的接收端
//1.创建DatagramSocket对象
MulticastSocket ms = new MulticastSocket(10086);
//把当前主机加入到一组ip
ms.joinGroup(InetAddress.getByName("224.0.1.0"));
//2.创建数据包DatagramPacet对象
byte[] bs = new byte[1024];
DatagramPacket dp = new DatagramPacket(bs,bs.length);
//3.接收数据,数据会存储到DatagramPacet数据包中
ms.receive(dp); //等待发送端发数据
//4.拆包,获取数据等信息
byte[] data = dp.getData(); //获取字节数组
int length = dp.getLength(); //获取有效的字节个数
InetAddress address = dp.getAddress(); //获取发送端的ip
//把字节数组中有效的部分转换为字符串
String str=new String(data,0,length);
System.out.println(address+"-->"+ str);
//5.释放资源
ms.close();
- 广播的发送端
//1.创建DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
//2.创建数据包DatagramPacket对象
byte[] bytes = "你好啊".getBytes(); //需要发送的数据
int length = bytes.length; //数据的长度
InetAddress address = InetAddress.getByName("255.255.255.255"); //广播地址
int port =10086; //对方的端口号
//把前面准备的好数据,以及ip地址,端口号 封装为对象
DatagramPacket dp = new DatagramPacket(bytes,length,address,port);
//3.发送数据包
ds.send(dp);
//4.释放资源
ds.close();
TCP发送和接收
TCP通信底层是通过IO流来实现的,Socket客户端,ServerSocket服务端。服务端是为客户端提供服务的,需要先开启服务端,然后由客户端过来连接。
客户端
public class TCPClient {
public static void main(String[] args) throws IOException {
//创建Socket对象,连接服务端
Socket s = new Socket("127.0.0.1", 10010);
//获取输出流,用来往远程服务器写数据
OutputStream out = s.getOutputStream();
out.write("Hello".getBytes());
//获取输入流,用来读取服务器返回的数据
InputStream in = s.getInputStream();
byte[] bs = new byte[1024];
int len = in.read(bs); //有效的字节是个数
System.out.println(new String(bs, 0, len));
//释放资源
s.close();
}
}
服务端
public class TCPServer {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(10010);
//监听客户端(迎宾)
Socket s = ss.accept();
//获取输入流
InputStream in = s.getInputStream();
//读取客户端发过来的数据
byte[] bs = new byte[1024];
int len = in.read(bs);
System.out.println(new String(bs, 0, len));
//获取输出流
OutputStream out = s.getOutputStream();
out.write("你好".getBytes());
//释放资源
s.close();
ss.close();
}
}
TCP上传图片
客户端
/*
图片上传的客户端
*/
public class UploadClient {
public static void main(String[] args) throws IOException {
//创建Socket对象
Socket socket = new Socket("127.0.0.1", 10011);
//获取输出流,用来往服务端写数据
OutputStream out = socket.getOutputStream();
//读取本地的图片,往服务写
FileInputStream fis = new FileInputStream("C:\\Users\\itheima\\Desktop\\a.jpg");
byte[] bs = new byte[1024];
int len; //记录每次读取的字节个数
while ((len = fis.read(bs)) != -1) {
//把读到的数据,通过网络流写到服务器
out.write(bs, 0, len);
}
//写一个结束标记
socket.shutdownOutput();
fis.close();
//获取输入流
InputStream in = socket.getInputStream();
//把字节输入流转换为字符输入流
InputStreamReader isr = new InputStreamReader(in);
//把字符输入流包装成缓冲流
BufferedReader br = new BufferedReader(isr);
//读取服务端返回的数据
String result = br.readLine();
System.out.println(result);
//释放资源
socket.close();
}
}
服务端
/*
图片上传的服务端
*/
public class UploadServer {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket ss=new ServerSocket(10011);
while (true){
//监听客户端
Socket socket = ss.accept();
//创建FileOutputStream,用来往服务端本地写数据
String filename = UUID.randomUUID().toString();
FileOutputStream fos=new FileOutputStream("C:\\Users\\itheima\\Desktop\\upload\\"+filename+".jpg");
//获取输入流
InputStream in = socket.getInputStream();
byte[] bs=new byte[1024];
int len;
while ((len=in.read(bs))!=-1){
//把读取到的数据,往服务本地的文件中写
fos.write(bs,0,len);
}
fos.close();
//获取字节输出流
OutputStream out = socket.getOutputStream();
//把字节输出流转换为字符输出流
OutputStreamWriter osw=new OutputStreamWriter(out);
//把字符输出流包装成缓冲流
BufferedWriter bw=new BufferedWriter(osw);
//往客户端回写数据
bw.write("上传成功");
bw.newLine();
bw.flush();
//释放资源
socket.close();
}
//ss.close();
}
}
多线程改进服务端
把服务端上传文件的代码写成线程任务,给多个线程共享。
/*
UploadRunnable是用来处理文件上传的任务
是给多个客户端服务的
*/
public class UploadRunnable implements Runnable{
private Socket socket;
//创建UploadRunnable对象时,要给Socket赋值
public UploadRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
//创建FileOutputStream,用来往服务端本地写数据
String filename = UUID.randomUUID().toString();
FileOutputStream fos= null;
try {
fos = new FileOutputStream("C:\\Users\\itheima\\Desktop\\upload\\"+filename+".jpg");
//获取输入流
InputStream in = socket.getInputStream();
byte[] bs=new byte[1024];
int len;
while ((len=in.read(bs))!=-1){
//把读取到的数据,往服务本地的文件中写
fos.write(bs,0,len);
}
fos.close();
//获取字节输出流
OutputStream out = socket.getOutputStream();
//把字节输出流转换为字符输出流
OutputStreamWriter osw=new OutputStreamWriter(out);
//把字符输出流包装成缓冲流
BufferedWriter bw=new BufferedWriter(osw);
//往客户端回写数据
bw.write("上传成功");
bw.newLine();
bw.flush();
InetAddress address = this.socket.getInetAddress();
System.out.println(address+"上传了一张图片");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放资源
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 每次监听到客户端连接服务器,就开启一条线程
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket ss=new ServerSocket(10011);
while (true){
//监听客户端
Socket socket = ss.accept();
//给Socket客户端,单独开线程
new Thread(new UploadRunnable(socket)).start();
}
//ss.close();
}
- 线程池改进服务端
/*
图片上传的服务端
*/
public class UploadServer {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket ss = new ServerSocket(10011);
//创建线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
6, //核心线程数
9, //最大线程数
3, //空闲时间
TimeUnit.SECONDS, //时间单位:秒
new ArrayBlockingQueue<>(10),
Executors.defaultThreadFactory(), //默认的工厂对象,由工厂对象帮我们生产线程
new ThreadPoolExecutor.AbortPolicy() //拒绝的方式
);
while (true) {
//监听客户端
Socket socket = ss.accept();
//给Socket客户端,单独开线程
poolExecutor.submit(new UploadRunnable(socket));
}
//ss.close();
}
}