网络编程
- 文件的上传下载实际就是读入写出的过程
- 如果是文本文件建议用字符流,如果是图片或者其他的文件,建议用字节流+处理流
IP、端口号和Socket
-
目的:直接或间接的通过网络协议与其他计算机实现数据交换,通讯
-
两个问题:
- 如何准确的定位到网络上的一台或多台主机,或者定位到主机上特定的应用
- IP
- 端口号
- 找到主机后如何可靠高效的进行数据传输
- OSI参考模型:
- 通讯协议
- 如何准确的定位到网络上的一台或多台主机,或者定位到主机上特定的应用
-
网络通信协议
- OSI参考模型:
- 应用层:直接面向用户的一层,用户的通信内容要由应用进程解决
- 表示层:应用过程之间传送的信息提供表示方法的服务。
- 会话层:负责维护两个节点之间的传输联接,确保点到点传输不中断,以及管理数据交换等功能
- 传输层:数据传输
- 网络层:数据在节点之间传输创建逻辑链路,通过路由选择算法为分组选择最佳路径,从而实现拥塞控制、网络互联等功能
- 数据链路层:通信实体问建立数据链路联接,传输的基本单位为“帧”
- 物理层:最底层,物理层传输的基本单位是比特流
- 实际上的TCP/IP参考模型
- 应用层:HTTP、FTP、DNS
- 传输层:TCP、UCP
- 网络层:IP、ICMP、ARP
- 物理+数据链路层:Link
- OSI参考模型:
-
IP地址:InetAddress类
- Java中Inetaddress来代表一个类,一个对象就是一个IP
-
InetAddress常用方法
- 静态方法:
- getByName(String str);获取IP对象
- getLocalhost();获取本地ip
- 非静态常用方法:
- getHostName();获取ip对象的域名
- getHostAddress(); 获取地址
- 静态方法:
public static void main(String[] args) {
try {
InetAddress ip = InetAddress.getByName("www.baidu.com");
System.out.println(ip);//www.baidu.com/61.135.169.121
InetAddress ip2 = InetAddress.getByName("192.168.1.1");
System.out.println(ip2);///192.168.1.1
InetAddress ip3 = InetAddress.getLocalHost();
System.out.println(ip3);//LAPTOP-IA3LU4GJ/169.254.193.252
//获取域名
String yuming = ip.getHostName();
System.out.println(yuming);//www.baidu.com
//获取ip地址
String ipAddress = ip.getHostAddress();
System.out.println(ipAddress);//61.135.169.125
} catch (Exception e) {
e.printStackTrace();
}
}
- 端口号:
- 标识正在计算机上运行的进程
- 不同的进程有不同的端口号
- tomcat默认占用8080,HTTP一般占用80,FTP一般是21,mysql3306
- 端口号和IP共同构成一个网络套接字:
- Socket
通讯协议,TCP和UDP
-
TCP:使用TCP协议前,会建立TCP连接,形成传输数据通道
- 传输使用三次握手方式,点对点通信,可靠的
- TCP协议进行通信的两个应用程序:客户端、服务端
- 在连接中可进行大数据量的传输
- 传输完毕,需要释放连接,效率低 -
UDP:将数据、源、目的封装成数据包,不需要建立连接
- 每个数据包大小在64k内
- 只管发,不管其他的,接收方也不确认,不可靠传输
- 可以广播发送
- 无需释放资源,效率高,速度快 -
三次握手(TCP建立可靠连接的依据/实现)
- 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号
- 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(seq=k),即SYN+ACK包,此时服务器进入SYN_RECV状态
- 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手
-
四次挥手(连接终止协议)
- TCP客户端发送一个FIN终止报文,用来关闭客户到服务器的数据传送。
- 服务器收到这个FIN终止报文,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
- 服务器关闭客户端的连接,发送一个FIN给客户端。
- 客户端发回ACK报文确认,并将确认序号设置为收到序号加1。
实现TCP的网络编程
-
客户端发送信息给服务器端发送信息,服务器端将数据显示在控制台
-
服务器端:
- 创建一个服务器端套接字,用于绑定端口号
- 设置该端口为可接收状态
- 给服务器端的套接字端输入流对象,用于读取数据
- 循环读取数据,通过ByteArrayOutputStream类写入数据,而是写到了该类中
- 关闭资源
-
客户端:
- 创建ip地址对象找到ip
- 创建套接字对象,写入ip地址和端口号
- 给套接字一个输出流,用于写出数据
- 写出数据到实体流中
- 关闭资源
public void Server(){
//1. 创建服务器端端口对象
ServerSocket ss = new ServerSocket(8899);
//2. 设置该端口为可接收状态
Socket socket = ss.accept();
//3. 获取输入流 ---Returns an input stream for this socket.
InputStream is = socket.getInputStream();
//4. 从务器端写出数据, 他没有指定写入到哪,而是写到了该类中的一个数组中
ByteArrayOutputStream bis = new ByteArrayOutputStream();
byte[] bytes = new byte[10];
int len;
while((len = is.read(bytes)) != -1){
bis.write(bytes,0,len);
}
//打印到控制台
System.out.println(bis.toString());
//5. 关闭资源
bis.close();
is.close();
socket.close();
ss.close();
}
- TCP编程:客户端发送文件给服务器端,服务器端把该文件存入到本地
/**
* 服务器端从socket的输出流中,接收数据,读取,然后输出流存储到本地
*/
public void Server(){
//1. 创建端口
ServerSocket sso = new ServerSocket(8899);
//2. 设置端口为可接收状态
Socket socket = sso.accept();
//3. 创建接收流,用于接收socket信息
InputStream is = socket.getInputStream();//接受流
//4. 创建输出流,用于将读取的文件存入本地
FileOutputStream fos = new FileOutputStream(new File("D:\\IdeaProjects\\JavaDemo\\sou\\图片05.jpg"));
//接收流开始读取数据
byte[] bytes = new byte[1024];
int len;
while((len = is.read(bytes)) != null){
//输出流,写入文件内容
fos.write(bytes,0,len);
}
//5. 关闭资源
fos.close();
is.close();
socket.close();
}
/**
* 客户端从本地读取一个文件,传输到socket中,发送给服务器端
*/
@Test
public void Client(){
//1. 发送前写清ip地址和端口号
InetAddress ip = InetAddress.getByName("127.0.0.1");
Socket socket = new Socket(ip,8890);
//2. 创建输出流,用于输出读取的文件
OutputStream os = socket.getOutputStream();//输出流
//3. 创建输入流,读取本地文件写入到输出流中
FileInputStream fis = new FileInputStream(new File("D:\\IdeaProjects\\JavaDemo\\sou\\图片04.jpg"));
//读入 写出
byte[] bytes = new byte[1024];
int len;
while((len = fis.read(bytes)) !=null){
//写出文件内容到socket中
os.write(bytes,0,len);
}
//4. 关闭资源
fis.close();
os.close();
socket.close();
}
- 在2基础上添加 服务器回复“图片已上传,谢谢”这句话,客户端接收
/**
* 服务器端从socket的输出流中,接收数据,读取,然后输出流存储到本地
*/
public void Server(){
//1. 创建端口
ServerSocket sso = new ServerSocket(8899);
//2. 设置端口为可接收状态
Socket socket = sso.accept();
//3. 创建接收流,用于接收socket信息
InputStream is = socket.getInputStream();//接受流
//4. 创建输出流,用于将读取的文件存入本地
FileOutputStream fos = new FileOutputStream(new File("D:\\IdeaProjects\\JavaDemo\\sou\\图片05.jpg"));
//接收流开始读取数据
byte[] bytes = new byte[1024];
int len;
while((len = is.read(bytes)) != null){
//输出流,写入文件内容
fos.write(bytes,0,len);
}
//5.服务器端接收完毕,发送一段话给客户端
OutputStream os = socket.getOutputStream();//写出
os.write("图片已上传,谢谢".getBytes());
//6. 关闭资源
os.close();
fos.close();
is.close();
socket.close();
}
/**
* 客户端从本地读取一个文件,传输到socket中,发送给服务器端
*/
@Test
public void Client(){
//1. 发送前写清ip地址和端口号
InetAddress ip = InetAddress.getByName("127.0.0.1");
Socket socket = new Socket(ip,8890);
//2. 创建输出流,用于输出读取的文件
OutputStream os = socket.getOutputStream();//输出流
//3. 创建输入流,读取本地文件写入到输出流中
FileInputStream fis = new FileInputStream(new File("D:\\IdeaProjects\\JavaDemo\\sou\\图片04.jpg"));
//读入 写出
byte[] bytes = new byte[1024];
int len;
while((len = fis.read(bytes)) !=null){
//写出文件内容到socket中
os.write(bytes,0,len);
}
//4. 客户端接收服务器发送的数据,并读取
InputStream is = socket.getInputStream();//读入
ByteArrayOutputStream baos = new ByteArrayOutputStream();//写出到控制台
byte[] bytes1 = new bytes[1024];
int len1;
while((len1 = is.read(bytes1) != -1){
baos.write(bytes1,0,len);
}
System.out.println(baos.toString());
//5. 关闭资源
baos.close();
is.close();
fis.close();
os.close();
socket.close();
}
UDP编程
/**
* UDP网络编程 发送端和接收端
*/
@Test
public void sender(){
DatagramSocket socket = null;
try {
//1. 创建一个socket来发送
socket = new DatagramSocket();
//2. 把一些信息打包给DatagramPacket
String str = "这是一个UDP数据";
byte[] data = str.getBytes();
InetAddress ip = InetAddress.getByName("127.0.0.1");
// 把一切信息都打包起来发送
DatagramPacket packet = new DatagramPacket(data,0,data.length,ip,8850);
//3. 将包由socket发送
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(socket != null){
//4. 关闭资源
socket.close();
}
}
}
@Test
public void receiver(){
DatagramSocket socket = null;
try {
//1. 创建socket
socket = new DatagramSocket();
//2. 接收包,用数组存储数据
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data,0,data.length);
//3. 接收包数据,从0到数组长度
socket.receive(packet);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(socket != null){
//4. 关闭资源
socket.close();
}
}
}
下载案例
- URL下载案例,tomcat下webapps中存放静态资源,
- 创建url对象,找到资源路径
- 创建url连接
- 先打开连接,返回连接对象
- 再通过连接对象进行连接
- 读入(从流中读取文件信息)
- 写出(边读边写到本地硬盘中)
- 关闭连接
public void downloadTest(){
//1. 指定URL资源
URL url = new URL("http://localhost:8080/example/ju.png");
//2. 返回一个url打开连接对象
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
//3. 连接
urlConnection.connect();
InputStream is = urlConnection.getInputStream();
FileOutputStream fos = new FileOutputStream("蓝屏01.png");
byte[] bytes = new byte[1024];
int len;
while((len = is.read(bytes)) != -1){
fos.write(bytes,0,len);
}
fos.close();
is.close();
urlConnection.disconnect();
}