Java网络编程
一.InetAddress类
此类没有构造方法,表示互联网协议 (IP) 地址。
如果一个类中没有构造方法:
A:这个类里面的成员方法都是静态(Math,Arrays,Collections)
B:单例模式:在内存始终只有一个对象
将构造方法私有化
在成员变量创建该类的实例(并且这个实例私有化被static修身)
提供该类公共方法可以通过外界调用
C:该类中会有某些静态成员方法的返回值是该类本身(InetAddress)
常用方法:
public static InetAddress getByName(String host)throws UnknownHostException在给定主机名的情况下确定主机的 IP 地址
public String getHostName()获取此 IP 地址的主机名。
public String getHostAddress()返回 IP 地址字符串
import java.io.IOException;
import java.net.InetAddress;
public class Demo {
public static void main(String[] args) throws IOException {
//获取IP地址
InetAddress address = InetAddress.getByName("DESKTOP-FN4C53P");
System.out.println(address.getHostAddress());//172.19.25.233
//获取主机名
String name = address.getHostName();
System.out.println(name);//DESKTOP-FN4C53P
}
}
二.网络编程的三要素
1)找到IP地址
2)必须有端口
3)必须有协议(TCP协议,UDP协议)
IP地址:
PC机器都有唯一的标识:ip
常见的IP:
A类:1.0.0.1---127.255.255.254:第一个号段为网络号段,后三个号段为主机号段
B类:128.0.0.1---191.255.255.254:前两个号段为网络号段,后两个号段为主机号段()
C类:192.0.0.1---223.255.255.254:前三个好号段为网络号段,后一个号段为主机号段
D类
224.0.0.1---239.255.255.254
E类
240.0.0.1---247.255.255.254
127.0.0.1:回环地址(也可表示本机地址)
xxx.xxx.xxx.255:广播地址
常用的命令:
ipconfig:查看ip
ping ip地址:查看当前本机与这台PC机器的网络通信
端口:
如果一台机器想要和另一台进行通信的话,光有ip是不行的,还必须知道端口号是多少
有效端口号:0~65535
保留端口号:0~1024
协议:
TCP协议和UDP协议
TCP协议:(客户端和服务器端的交互)
1)建立连接通道(通道内的流:(使用的是最基本的字节流))
2)可靠协议
3)由于可靠协议,并且需要建立连接通道(服务器端需要等待客户端连接),执行效率低
4)使用TCP协议(TCP编程)可以发送大量的数据,文件大小无限制
UDP协议:(发送端和接收端)
1)无需建立连接通道(数据报包的形式发送数据的)
2)不可靠协议
3)由于不可靠,无需建立连接通道,所以它的执行效率高
4)UDP协议(UDP编程)发送数据有限制
Socket()对象:
public class Socket
extends Object
此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
套接字的实际工作由 SocketImpl 类的实例执行。应用程序通过更改创建套接字实现的套接字工厂可以配置它自身,以创建适合本地防火墙的套接字。
三. UDP协议
UDP发送端 :
1)创建UDP协议发送端的Socket对象
2)创建数据报包:通过这个数据包将数据发送到接收端
3)调用UDP协议发送端发送的方法
4)关闭资源
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SendDemo {
public static void main(String[] args) throws IOException {
//public DatagramSocket() throws SocketException
//创建Socket()对象
DatagramSocket ds = new DatagramSocket();
//创建数据报包
byte [] bys = "原谅我这一生放浪不羁爱自由..".getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length,
InetAddress.getByName("127.0.0.1"),12345);
//将数据以数据报包的形式发出去
ds.send(dp);
//关闭资源
ds.close();
}
}
UDP协议的接收端
1)创建接收端的Socket对象
2)创建一个数据报包接收发送端发送来的数据报包(接收容器)
3)接收数据,调用DataScoket类中的接收的方法来接收数据包
4)解析数据报包里面的实际数据,显示在控制台上
5)关闭资源
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//创建接收端的Socket对象
DatagramSocket ds = new DatagramSocket(12345);
//创建一个接收容器接收数据
byte [] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
//接收数据
ds.receive(dp);
//解析数据并显示在控制台
String ip = dp.getAddress().getHostAddress();
//获取缓存区中的实际数据
byte[] bys2 = dp.getData() ;
//获取实际缓冲区数据的长度
int len = dp.getLength();
String s = new String(bys2, 0, len);
//输出在控制台
System.out.println(ip+"传递的数据是:"+s);
//关闭资源
ds.close() ;
}
}
练习:发送端的数据来源不是简单的一条语句而是不停的键盘录入
键盘录入的方式:
1)Scanner类
2)BufferedReader类(字符缓冲流)特有功能:readLine():一次读取一行 (System.in)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SendDemo {
public static void main(String[] args) throws IOException {
//创建发送端的Socket对象
DatagramSocket ds = new DatagramSocket();
//创建键盘录入,以IO流的形式
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
String line = null;
while((line = br.readLine()) != null) {
if("over".equals(line)) {
break;
}
//创建数据报包
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length,
InetAddress.getByName("127.0.0.1"),12345);
//发送数据
ds.send(dp);
}
//释放资源
ds.close();
}
}
接受端:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//创建接收端的socket对象
DatagramSocket ds = new DatagramSocket(12345);
while(true) {
byte [] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
//接收数据
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
//解析数据接收容器,获取里面实际的内容
byte[] data = dp.getData();
int len = dp.getLength();
String s = new String(data, 0, len);
//显示到控制台
System.out.println("form"+ip+"Data is :"+s);
}
}
}
四.TCP协议:
客户端步骤:
1)创建客户端的socket对象 (客户端套接字流的形式)
2)获取通道的内流(输出流)
3)使用输出流对象写数据
4)关闭客户端的Socket对象
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
/**
* 使用Socket编程里面的TCP协议
* 客户端和服务器端的交互
* 客户端步骤:
* 1)创建客户端的socket对象 (客户端套接字流的形式)
* 2)获取通道的内流(输出流)
* 3)使用输出流对象写数据
* 4)关闭客户端的Socket对象
*
* */
public class ClientDemo {
public static void main(String[] args) throws IOException {
//1)创建客户端的socket对象:此类实现客户端套接字
Socket s = new Socket("172.19.25.233",10000);
/*InetAddress address = InetAddress.getByName("DESKTOP-FN4C53P");
System.out.println(address.getHostAddress());*/
//2)获取通道的内流(输出流)
OutputStream os = s.getOutputStream();
//3)客户端通道内的流给服务器写数据
os.write("爱恨情仇都付谈笑".getBytes());
//4)关闭客户端socket
s.close();
}
}
TCP协议的服务器端
1)创建服务器端的socket对象,指定端口
2)服务器端需要监听客户端的连接
3)获取通道内的输入流
4)将数据获取到并显示到控制台
5)关闭服务器端的socket资源
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* TCP协议的服务器端
*
* 1)创建服务器端的socket对象,指定端口
* 2)服务器端需要监听客户端的连接
* 3)获取通道内的输入流
* 4)将数据获取到并显示到控制台
* 5)关闭服务器端的socket资源
*
* */
public class ServerDemo {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
//public ServerSocket(int port) 构造方法:创建绑定到特定端口的服务器套接字
ServerSocket ss = new ServerSocket(10000);
System.out.println("等待客户端连接...");
//2)监听客户端的连接:public Socket accept()请求等待介入
Socket s = ss.accept();
System.out.println("客户端已连接...");
//3)获取通道内的输入流
InputStream in = s.getInputStream();
//4)将数据获取到并显示到控制台
//定义一个缓冲数组
byte[] bys = new byte[1024];
int len = in.read(bys);
String str = new String(bys, 0, len);
//获取IP地址的文本形式
InetAddress address = s.getInetAddress();
String ip = address.getHostAddress();
System.out.println(ip+"传递的数据是:"+str);
s.close();
}
}
客户端发送数据,服务器端给客户端反馈数据
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* 客户端发送数据,服务器端给客户端反馈数据...
* */
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象
Socket s = new Socket("172.19.25.233",12580);
//获取通道内的输出流
OutputStream os = s.getOutputStream();
os.write("上元溪旁点烛荷 千盏承诺".getBytes());
//获取通道内内的输入流对象
InputStream in = s.getInputStream();
byte [] bys = new byte[1024];
int len = in.read(bys);
String str = new String(bys, 0, len);
System.out.println("clinet:"+str);
s.close();
}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
//1)创建服务器端的socket对象
ServerSocket ss = new ServerSocket(12580);
System.out.println("等待客户端连接...");
Socket s = ss.accept();
System.out.println("客户端已连接....");
InputStream in = s.getInputStream();
byte[] bys2 = new byte [1024];
int len2 = in.read(bys2);
String server = new String(bys2, 0, len2);
//输出客户端给服务器端发送的数据
System.out.println("server:"+server);
//服务器端反馈
OutputStream out= s.getOutputStream();
out.write("[自动回复]:您好,我现在有事,不方便回复!".getBytes());
//关闭资源
s.close();
}
}
需求:客户端键盘录入数据,服务器端将数据显示控制台
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class ClientDemo {
public static void main(String[] args) throws IOException {
Socket s = new Socket("172.19.25.233",11000);
//BufferedReader字符流封装数据,读取键盘录入的数据
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
//使用字符缓冲输出流封装通道内的流
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
//利用BufferedReader一次读取一行数据:readLine
String line = null;
while((line = br.readLine()) != null) {
//结束条件
if("over".equals(line)) {
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
s.close() ;
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
//1)创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(11000);
System.out.println("等待服务器连接...");
//2)监听客户端的连接
Socket s = ss.accept();
System.out.println("服务器已连接...");
//3)封装服务器通道内的流
BufferedReader br = new BufferedReader(
new InputStreamReader(s.getInputStream()));
String line = null;
while((line = br.readLine()) != null) {
System.out.println(line);
}
//关闭资源
s.close();
}
}
客户端键盘录入,服务器输出将录入的内容输出到文本文件
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class ClientDemo {
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1",6666);
//键盘录入:IO流的形式
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
//封装通道内的流
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
String line = null;
while((line = br.readLine()) != null) {
if("over".equals(line)) {
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
s.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(6666);
//监听客户端连接
Socket s = ss.accept();
//封装通道内的流
BufferedReader br = new BufferedReader(
new InputStreamReader(s.getInputStream()));
//服务器端输出一个文本文件
BufferedWriter bw = new BufferedWriter(
new FileWriter("a.txt"));
//服务器端通道内的流读取客户端发送的数据,将数据一次写入到a.txt文件中
String line = null;
while((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
s.close();
bw.close();
}
}
需求:客户端一个文本文件,服务器端将客户端文本文件中的内容展示到控制台上
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象
Socket s = new Socket("127.0.0.1",12345);
//封装文本文件
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//封装通道内的流:将字节输出流封装成字符缓冲输出流
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
//将文本文件中的数据写入到被封装的通道内的流中
String line = null;
while((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
br.close();
s.close();
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
//创建服务器端Socket()对象
ServerSocket ss = new ServerSocket(12345);
//监听客户端
Socket s = ss.accept();
//封装通道内的流
BufferedReader br = new BufferedReader(
new InputStreamReader(s.getInputStream()));
//读取一行数据,就展示到控制台上
String line = null;
while((line = br.readLine()) != null) {
System.out.println(line);
}
//释放资源
s.close();
}
}
客户端文本文件,服务器端将文本文件中的内容复制输出到一个新的文本文件中
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class UploadClient {
public static void main(String[] args) throws IOException {
//创建客户端的socket对象
Socket s = new Socket("127.0.0.1",12580);
//封装文件
@SuppressWarnings("resource")
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//封装通道内字节输出流
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
//一次读取一行
String line = null;
while((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
s.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadServer {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
//创建服务器端的Socket()对象
ServerSocket ss = new ServerSocket(12580);
//监听客户端
Socket s = ss.accept();
//封装通道内的流
BufferedReader br = new BufferedReader(
new InputStreamReader(s.getInputStream()));
//服务器端要将输出文本文件进行复制
BufferedWriter bw = new BufferedWriter(new FileWriter("copy.txt"));
//一次读取一行
String line = null;
while((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
s.close();
bw.close();
}
}
客户端文本文件,服务器端将文本文件中的内容复制输出到一个新的文本文件中,并且服务器端反馈给客户端,文件复制成功了...
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class UploadClient {
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1",12345);
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//封装通道内的流
BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
//一次读取一行
String line = null ;
while((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
/*//方案一:定义一个结束标记
bw.write("over");
bw.newLine();
bw.flush();*/
//方案二通过这个方法告诉服务器端,客户端这边没数据了,读完了,你反馈
s.shutdownOutput();
//读取服务器端的反馈
//获取通道内的输入流对象
BufferedReader brClient = new BufferedReader(
new InputStreamReader(s.getInputStream()));
//读数据
String fk = brClient.readLine();
System.out.println(fk);
//释放资源
s.close();
br.close();
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadServer {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
//创建服务器端的socket对象
ServerSocket ss = new ServerSocket(12345);
//监听客户端连接
Socket s = ss.accept();
//封装通道内的流
BufferedReader br = new BufferedReader(
new InputStreamReader(s.getInputStream()));
BufferedWriter bw = new BufferedWriter(new FileWriter("copy2.txt"));
String line = null;
while((line = br.readLine()) != null) {//读到的null的情况,文件读完毕 ,阻塞
/*if("over".equals(line)) {
break;
}*/
bw.write(line);
bw.newLine();
bw.flush();
}
//反馈:获取通道内的输出流对象,并且用BufferedWrite封装
BufferedWriter bwServer = new BufferedWriter(
new OutputStreamWriter(s.getOutputStream()));
bwServer.write("文件复制完毕....");
bwServer.newLine();
bwServer.flush();
//释放资源
s.close();
bw.close();
}
}
注意:按照正常的逻辑操作写完了客户端和服务器的需求,运行的时候,发现都处于等待情况,是什么原因呢?
对于客户端来说,它要将java文件写入到字符输出流中(通道内的流),在最终返回如果是null(null在网络编程中是不能作为结束条件的)的情况下,文件读完了,但是服务器端不指定
客户端这边java文件是否读完了,两端readLine都处于阻塞,如何解决?
方案1:在客户端这边定义结束标记,服务器端只要读到结束标记了,就表示读完了.
该方式虽然可以,但是可能文件中有某句话就是自定义结束语句,可能文件压根没有读完就结束了!,不够好!
方案2:public void shutdownOutput() throws IOException禁用此套接字的输出流
客户端的图片文件,服务器端输出一个图片文件并且给出反馈
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
/**
* 客户端的图片文件,服务器端输出一个图片文件并且给出反馈
*
*/
public class UploadClient {
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1",6666);
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("D:\\壁纸.jpg"));
//获取通道内的流 输出流 OutputStream
BufferedOutputStream bos = new BufferedOutputStream(
s.getOutputStream());
//获取通道内的流 输出流 OutputStream
byte [] bys = new byte[1024];
int len = 0;
while((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
//刷新
bos.flush();
}
s.shutdownOutput();
//客户端读取服务器的反馈
InputStream is = s.getInputStream();
byte [] bys2 = new byte[1024];
int len2 = is.read(bys2);
System.out.println(new String(bys2, 0, len2));
bis.close();
s.close();
}
}
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class UploadServer {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
//创建服务器端Socket()对象
ServerSocket ss = new ServerSocket(6666);
//监听客户端
Socket s = ss.accept();
//创建通道流
BufferedInputStream bis = new BufferedInputStream(
s.getInputStream());
//输出图片文件
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("copy.jpg"));
//读取
byte [] bys = new byte[1024];
int len = 0;
while((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}
//服务器端给客户端反馈
OutputStream os = s.getOutputStream();
os.write("文件传输成功...".getBytes());
//释放资源
s.close();
bos.close();
}
}