java网络编程基础:
一、网络基础
1.网络通信
概念:两台主机之间通过网络实现通信(数据传输)。
java.net包下提供了网络通信的一系列类或接口。
2.网络
概念:两台或多态设备通过一些物理设备连接形成网络。
分类:(根据覆盖范围不同)
局域网:一个教室或机房。
城域网:覆盖一个城市。
广域网:全国甚至全球。
3.ip地址
概念:唯一标识网络中的每台计算机。
表示:点分十进制 xx.xx.xx.xx (每一个十进制数的范围0~255)。
组成:网络地址+主机地址,如192.168.16.69
ipv4和ipv6:
ipv4:网络地址有限
ipv6:替代ipv4的下一代ip协议,地址数量号称可以为全世界的每一粒沙子都编上一个地址。
物联网:称万物皆可联网,ipv6的出现解决了多种接入设备连入互联网的问题。
ipv6为:冒分16进制。
4.ipv4地址分类
abc的网络号分别占1个,2个,3个字节。
5.域名
www.baidu.com
因ip地址不好记,为了方便记忆,将ip地址映射成了域名。
6.端口号
概念:表示计算机上某个特定的网络程序。
表示形式:整数,范围在0~65535
0~1024端口号已经被占用,比如 ssh 22,ftp 21,smtp 25,http 80
常见的网络程序端口号:
tomcat:8080
mysql:3306
oracle:1521
sqlserver:1433
7.网络通信协议
(1)tcp/ip协议
tcp/ip协议:传输控制协议/互联网协议,又叫网络通讯协议,是internet最基本的协议。
比如任何人直接的交流(通讯)靠语言,那么语言就是一种协议。 现在计算机和计算机之间的传递数据,也是按照一种规定好的协议方式。
(2)网络通信协议
8.TCP和UDP
(1)TCP协议
1.先建立TCP连接,形成传输数据通道。
2.传输前采用“三次握手”方式,是可靠的。
3.TCP通信时的两个应用进程:客户端/服务端。
4.在连接中可进行大数据量的传输。
5.传输完毕,须先释放建立的连接,效率较低。
(2)UDP协议
1.将数据、源、目的封装成数据包,不需要建立连接。
2.每个数据报大小在64K内。
3.因无需连接,故不可靠。
4.发送数据结束后不用释放资源,速度快。
5.例如发短信,通知。
二、InetAddress类
1.getLocalHost()可得到主机名和对应的本机的ip地址对象。
2.getByName()可根据主机名或域名得到对应的ip地址对象。
3.getHostName()->根据ip地址对象可分别得到其中的域名/主机名。
4.getHostAddress()->根据ip地址对象可分别得到其中的ip地址。
//1.获取本机的InetAddress对象,getLocalHost,可得到主机名和对应的本机的ip地址对象
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("1."+localHost);
//2.3.根据指定的主机名/域名获取ip地址对象 getByName,可得到根据域名或主机名 得到对应的 域名或主机名/ip地址
InetAddress host = InetAddress.getByName("DESKTOP-9OTH615");
System.out.println("2."+host);
InetAddress yuming = InetAddress.getByName("www.baidu.com");
System.out.println("3."+yuming);
//4.获取InetAddress对象的主机名getHostName->根据ip地址对象可分别得到其中的域名/主机名
String hostName = host.getHostName();
String yumingHostName = yuming.getHostName();
System.out.println("4."+hostName+" "+yumingHostName);
//5.获取InetAddress对象的地址getHostAddress->根据ip地址对象可分别得到其中的ip地址
String hostAddress = host.getHostAddress();
String hostAddress1 = yuming.getHostAddress();
System.out.println("5."+hostAddress+" "+hostAddress1);
三、Socket套接字
1.Socket开发网络应用程序被广泛采用,以至于成为事实上的标准。
2.通信的两端都要有Socket,是两台机器间通信的端点。
3.网络通信就是Socket间的通信。
4.Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。
5.一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。
Socket就像是两个电源之间的插头,而创建的连接就相当于电源线。
数据在两个Socket间通过IO传输:
四、TCP网络通信编程
1.基本介绍
(1)基于客户端/服务端的网络编程。
(2)底层使用TCP/IP协议。
(3)基于Socket的TCP编程。
2.使用字节流
/*
服务器
*/
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//在本机的9999端口监听,等待连接
//(要求在本机没有其他服务在监听9999端口)
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端在9999端口监听..,等待连接");
//当没有客户端连接9999端口时,程序会阻塞,等待连接
Socket socket = serverSocket.accept();
//当有客户端连接时,返回socket对象,程序继续
System.out.println("服务端连接成功");
//通过socket.getInputStream();读取数据通道的数据。
InputStream inputStream = socket.getInputStream();
byte[] bytes=new byte[1024];
int readLine=0;
while ((readLine=inputStream.read(bytes))!=-1){
System.out.println(new String(bytes,0,readLine));
}
//关闭流和socket
inputStream.close();
socket.close();
serverSocket.close();
}
}
/*
客户端
*/
public class SocketTCP01Client {
public static void main(String[] args) throws IOException {
//连接本机的9999端口,如果连接成功,则会返回一个socket对象
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端已连接,准备发送数据");
//通过socket.getOutputStream();得到与socket关联的输出流对象。
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,server".getBytes(StandardCharsets.UTF_8));
//关闭资源
outputStream.close();
socket.close();
System.out.println("客户端退出");
}
}
serverSocket可以创建很多的socket,它只要有一次accept就会有一个socket。
/*
服务端
*/
public class SocketTCP02Server {
public static void main(String[] args) throws IOException {
//z1
ServerSocket serverSocket = new ServerSocket(9998);
//z2
Socket socket = serverSocket.accept();
//z3
OutputStream os = socket.getOutputStream();
os.write("hello server".getBytes());
//设置输出结束标记
socket.shutdownOutput();
//接收服务端发送的数据 z4
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int readLine=0;
while ((readLine=is.read(bytes))!=-1){
System.out.println(new String(bytes,0,readLine));
}
//释放资源
is.close();
os.close();
socket.close();
serverSocket.close();
}
}
/*
客户端
*/
public class SocketTCP02Client {
public static void main(String[] args) throws IOException {
//z1
Socket socket = new Socket(InetAddress.getLocalHost(), 9998);
//z2
InputStream is = socket.getInputStream();
byte[] bytes = new byte[1024];
int readLen=0;
while ((readLen=is.read(bytes))!=-1){
System.out.println(new String(bytes,0,readLen));
}
//z3 发送hello client
OutputStream os = socket.getOutputStream();
os.write("hello client".getBytes());
//关闭资源
os.close();
is.close();
socket.close();
}
}
当写入结束时,设置一个写入结束标记:socket.shutdownOutput();
3.使用字符流
使用字符流进行编写:
/**
* 服务端
*/
public class SocketTCP03Server {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
InputStream is = socket.getInputStream();
//?IO读取,使用字符流 用InputStreamReader将InputStream转成字符流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String s = br.readLine();
System.out.println(s);//使用字符流读取后输出也比较方便。
System.out.println("Server读取完毕");
//回送给客户端信息
OutputStream os = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write("hello client,我是Server");
bw.newLine();
bw.flush();
//关闭资源
bw.close();//关闭流时,只需关闭外层流即可
br.close();
socket.close();
serverSocket.close();
}
}
/**
*客户端
*/
public class SocketTCP03Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
OutputStream os = socket.getOutputStream();
//1.使用字符流进行写入
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write("hello server,我是Client");
bw.newLine();//字符流也可以用newLine()作为自己的写入结束标记
bw.flush();//使用字符流写入,需要手动刷新,否则数据不会写入数据通道。
//2.接收信息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String s = br.readLine();
System.out.println(s);
//关闭资源
br.close();
bw.close();
socket.close();
}
}
即字节输入流转为字符输入流并进行输入:
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String s = br.readLine();
System.out.println(s);
字节输出流转为字符输出流并进行输出:
OutputStream os = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write("hello client,我是Server");
bw.newLine();
bw.flush();
4.网络上传图片
(1)基本介绍及思路分析
1.首先把磁盘上的文件读到程序里面去。
2.然后把图片放在一个字节数组,因为图片是二进制的,放在一个字节数组比较合理。
3.socket获取输出流。
4.传到服务端后,首先要通过一个socket获取一个输入流,先保存在一个服务器的字节数组(内存)中去。
服务端再获取一个输出流,将获取到的数据输出到一个指定的目录。(刷新打磁盘上。)
(2)具体实现
StreamUtils工具类:
/**
* 此类用于演示关于流的读写方法
*
*/
public class StreamUtils {
/**
* 功能:将输入流转换成byte[], 即可以把文件的内容读入到byte[]
* @param is
* @return
* @throws Exception
*/
public static byte[] streamToByteArray(InputStream is) throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
byte[] b = new byte[1024];//字节数组
int len;
while((len=is.read(b))!=-1){//循环读取
bos.write(b, 0, len);//把读取到的数据,写入bos
}
byte[] array = bos.toByteArray();//然后将bos 转成字节数组
bos.close();
return array;
}
/**
* 功能:将InputStream转换成String
* @param is
* @return
* @throws Exception
*/
public static String streamToString(InputStream is) throws Exception{
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder= new StringBuilder();
String line;
while((line=reader.readLine())!=null){
builder.append(line+"\r\n");
}
return builder.toString();
}
}
StreamUtils工具类:
1.将输入流转为字节数组。
2.将输入流转为字符串。
/**
* 服务端:将图片输出到磁盘上
*/
public class TCPFileUploadServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
//服务端接收客户端发来的图片
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);
//获取本机上的输出流
String descSrc = "src\\water.png";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descSrc));
//图片的字节数组写入输出流。
bos.write(bytes);
//输出完毕,发送收到图片给客户端
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("收到图片");
bw.newLine();
bw.flush();
//关闭资源
bw.close();
bis.close();
bos.close();
socket.close();
serverSocket.close();
}
}
/**
*将图片以字节数组的形式发给服务端
*/
public class TCPFileUploadClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
//1.客户端发送照片
//1.1读取磁盘上的文件
String path="D:\\桌面\\qie.png";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));
//1.2转为字节数组
byte[] bytes = StreamUtils.streamToByteArray(bis);
//2.将图片输出到服务端
OutputStream os = socket.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(os);
bos.write(bytes);
socket.shutdownOutput();//设置写入的结束标记
//收到服务端发送的信息,以字符流的形式发送,以字符流的形式输出
// BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// String s = br.readLine();
InputStream inputStream = socket.getInputStream();
String s = StreamUtils.streamToString(inputStream);
System.out.println(s);
//关闭资源
// br.close();
bos.close();
bis.close();
socket.close();
}
}
五、UDP网络通信编程
UDP在网络开发中用的较少,大体知道是怎么开发的,特点是什么就行。
(1)DatagramSocket和DatagramPacket
DatagramSocket:数据报套接字,用于发送和接收数据报。
DatagramPacket:封装了UDP数据报。包含发送端和接收端的ip和端口。
(2)基本介绍
1.DatagramSocket和DatagramPacket实现了基于UDP的网络编程程序。
2.UDP数据包封装在DatagramPacket中,通过DatagramSocket发送和接收,但不能保证UDP数据包一定能够安全的送到目的地,也不能确定什么时间可以抵达。
3.DatagramPacket封装UDP数据包对象中包含发送端和接收端的ip地址和端口号。
4.因为封装了各自的ip地址和端口号,因此无须建立发送方和接收方的连接。
(3)基本流程
UDP没有服务端和客户端的概念了,两者都可以是发送端和接收端。
(4)UDP案例
/**
*接收端A
*/
public class UDPReceiverA {
public static void main(String[] args) throws IOException {
//1.创建一个DatagramSocket对象,准备在9999接收数据
DatagramSocket socket = new DatagramSocket(9999);
//2.构建一个DatagramPacket对象,准备接收数据
//一个数据报最大为64K
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
//3.调用 接收方法,将通过网络传输的DatagramPacket对象 填充到packet对象
//若有数据包发送到9999端口,就会接收到数据
//否则,就会阻塞等待。
System.out.println("接收端A 等待接收数据");
socket.receive(packet);
//4.可以把packet进行拆包, 取出数据, 并显示
int length = packet.getLength();//实际接收到的数据字节长度
byte[] data = packet.getData();//接收到数据
String s = new String(data,0, length);//少了个0
System.out.println(s);
//4.2向B端发送资源
//5.关闭资源
socket.close();
System.out.println("A端退出");
}
}
/**
*发送端B
*/
public class UDPSenderB {
public static void main(String[] args) throws IOException {
//1.创建DatagramSocket 准备在9998端口接收数据
DatagramSocket socket = new DatagramSocket(9998);
//2.将需要发送的数据,封装到 DatagramPacket对象
byte[] data = "hello, 明天吃火锅".getBytes();
//说明:封装的 DatagramPacket对象 data内容字节数组 , data.length , 主机(IP) , 端口
DatagramPacket packet =
new DatagramPacket(data, data.length, InetAddress.getByName("10.66.116.28"), 9999);
socket.send(packet);
//关闭资源
socket.close();
System.out.println("B端退出");
}
}
作业
/**
* 服务端:将图片输出到磁盘上
*/
public class TCPFileUploadServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
//服务端接收客户端发来的图片
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] bytes = StreamUtils.streamToByteArray(bis);
//获取本机上的输出流
String descSrc = "src\\water.png";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(descSrc));
//图片的字节数组写入输出流。
bos.write(bytes);
//输出完毕,发送收到图片给客户端
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("收到图片");
bw.newLine();
bw.flush();
//关闭资源
bw.close();
bis.close();
bos.close();
socket.close();
serverSocket.close();
}
}
/**
*将图片以字节数组的形式发给服务端
*/
public class TCPFileUploadClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
//1.客户端发送照片
//1.1读取磁盘上的文件
String path="D:\\桌面\\qie.png";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));
//1.2转为字节数组
byte[] bytes = StreamUtils.streamToByteArray(bis);
//2.将图片输出到服务端
OutputStream os = socket.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(os);
bos.write(bytes);
socket.shutdownOutput();//设置写入的结束标记
//收到服务端发送的信息,以字符流的形式发送,以字符流的形式输出
// BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// String s = br.readLine();
InputStream inputStream = socket.getInputStream();
String s = StreamUtils.streamToString(inputStream);
System.out.println(s);
//关闭资源
// br.close();
bos.close();
bis.close();
socket.close();
}
}
工具类:
/**
* 此类用于演示关于流的读写方法
*
*/
public class StreamUtils {
/**
* 功能:将输入流转换成byte[], 即可以把文件的内容读入到byte[]
* @param is
* @return
* @throws Exception
*/
public static byte[] streamToByteArray(InputStream is) throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
byte[] b = new byte[1024];//字节数组
int len;
while((len=is.read(b))!=-1){//循环读取
bos.write(b, 0, len);//把读取到的数据,写入bos
}
byte[] array = bos.toByteArray();//然后将bos 转成字节数组
bos.close();
return array;
}
/**
* 功能:将InputStream转换成String
* @param is
* @return
* @throws Exception
*/
public static String streamToString(InputStream is) throws Exception{
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder= new StringBuilder();
String line;
while((line=reader.readLine())!=null){
builder.append(line+"\r\n");
}
return builder.toString();
}
}
六、代码学习思路
先理清思路,再开始自己写代码,遇到问题时再会看视频找出问题,最后再总结反思。
双向并举,一般同时学两个东西是最快的。