网络编程概述
java实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。即里面有类包,直接调用就可以了。
软件架构:
- C/S架构,客户端和服务端
- B/S架构,浏览器和服务器
无论哪种架构,都离不开网络的支持,网络编程,即在一定的协议下,实现两台计算机的通信的程序。
实际上一些C/S也融合了B/S,所以有未更新的客户端也能看到新的功能页面。
网络基础
网络编程的目的,直接或间接的通过网络协议与其他计算机实现数据交换,进行通讯。
实现网络通信,解决的问题:
- 如何准确定位网络上的一台主机或多台主机?-》IP地址
- 如何定位主机上特定的应用?-》端口号
- 如何可靠、高效的进行传输?-》规范网络通信协议
通信要素:IP地址和域名
IP:互联网协议地址,给网络中的设备作唯一的编号。
IP地址分类方式:IPV4,IPV6
-
了解IPV4和IPV6
-
公网地址和私有地址
- 192.168.开头的就是私有地址
本地回路地址:127.0.0.1就代表了本地本机IP地址了,不用ipconfig去查了。
域名解析:连接网络时输入主机域名,域名服务器Domain Name System将域名转化为IP地址,才能和主机建立连续
端口号:唯一标识设备中的进程
网络通信协议:常使用TCP/IP四层协议
InetAddress类的一个实例就代表一个具体的IP地址
掌握两个实例,如下示例:
public class InetAddressTest {
public static void main(String[] args) {
// 实例化
try {
InetAddress inet1 = InetAddress.getByName("192.168.1.1");
System.out.println(inet1);
InetAddress inet2 = InetAddress.getByName("www.baidu.com");
System.out.println(inet2);
// 获取本机地址
InetAddress inet3 = InetAddress.getLocalHost();
System.out.println(inet3);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
掌握两个方法:
// 获取域名
String hostName = inet2.getHostName();
String address = inet2.getHostAddress();// 获取ip地址
System.out.println(hostName);
System.out.println(address);
TCP与UDP协议剖析,TCP编程
java.net提供了低层次的通信细节,提供了常见的网络协议的支持:
-
UDP,用户数据表协议,
-
TCP,传输控制协议,
TCP三次握手:发送数据的准备阶段,客户端与服务端的三次交互,以保证连接的可靠性
-
第一次,客户端向服务端发起连接请求
-
第二次握手,服务端发送针对客户端TCP请求的确认
-
第三次,客户端发送确认的确认
TCP,释放时经过4次挥手,
Socket类:网络上具有唯一标识的IP地址和端口号组合在一起构成唯一能识别的标识符套接字socket。
网络通信实际就是socket间的通信。
TCP示例-客户端发送内容给服务端
package com.atguigu02.tcpudp;
import org.testng.annotations.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
/**
* @DESC: 客户端发送内容给服务端,服务端将内容输出
* 先启动服务端,再启动客户端
*/
public class TCPTest1 {
// 客户端
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
try {
// 创建一个socket
// InetAddress inetAddress = InetAddress.getByName("192.168.1.1");
InetAddress inetAddress = InetAddress.getLocalHost();
int port = 8989;
socket = new Socket(inetAddress, port);
// 发送数据
os = socket.getOutputStream();
os.write("你好客户端发来的数据".getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭socket
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 服务端
@Test
public void server() {
ServerSocket serverSocket = null;
Socket socket = null; // 阻塞式的方法
InputStream is = null;
try {
// 创建ServerSocket
int port = 8989; // 与客户端中请求的服务端的端口号一样
serverSocket = new ServerSocket(port);
// 调用accept方法,接受客户端的socket
socket = serverSocket.accept();
System.out.println("服务端已开启");
// 接收数据
is = socket.getInputStream();
// 回顾io流接收数据
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
// 字符串
String s = new String(buffer, 0, len);
System.out.println(s);
}
System.out.println("\n数据接收完毕");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭socket serversocket
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
如图所示:
注意服务端接收那里,字节流一般不处理文本文件,引入字节数组输出流,下面是修改的,
// 下面是修正后的接收内容
byte[] buffer = new byte[5];
int len;
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 内部维护了一个byte数组
while ((len = is.read(buffer)) != -1){
baos.write(buffer, 0, len);
}
System.out.println(baos.toString());
System.out.println("\n数据接收完毕");
客户端发送文件给服务端
还是同样的流程,注意读写过程需要掌握。
package com.atguigu02.tcpudp;
import org.testng.annotations.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @DESC: 客户端发送文件给服务端
*/
public class TCPTest2 {
@Test
public void client() {
Socket socket = null;
FileInputStream fis = null;
OutputStream os = null;
try {
// 创建socket
// socket需要服务端的ip地址或者域名,以及端口号
// 获取ip,定义端口号
InetAddress localHost = InetAddress.getLocalHost();
// InetAddress localHost = InetAddress.getByName('127.0.0.1');
int port = 9090;
socket = new Socket(localHost, port);
// 创建File的实例,FileInputStream的实例
File file = new File("E://mycodes//ideaProject//shangguigujavastudy//JavaSECode//chapter16_net//src//test.png");
// 创建输入流
fis = new FileInputStream(file);
// 通过Socket获取输出流
os = socket.getOutputStream();
// 读写数据
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1) {
// 写数据
os.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("客户端数据发送完毕");
// 关闭socket和其他流
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void server() {
ServerSocket serverSocket = null;
InputStream is = null;
FileOutputStream fos = null;
try {
// 创建serverSocket
int port = 9090;
serverSocket = new ServerSocket(port);
// 接收来自客户端的socket
Socket accept = serverSocket.accept();
// 获取一个输入流
is = accept.getInputStream();
// 同样也是文件,需要保存到服务端当中,使用到输出流保存
File file = new File("E://mycodes//ideaProject//shangguigujavastudy//JavaSECode//chapter16_net//src//test_server.png");
fos = new FileOutputStream(file);
// 读写过程,把接受到的写进去
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("服务端接收文件成功");
// 关闭相关的socket和流
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端发送文件给服务端并返回信息给客户端
客户端标记不再发送数据了,接收服务端返回的数据。
package com.atguigu02.tcpudp;
import org.testng.annotations.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @DESC: 客户端发送文件给服务端
*/
public class TCPTest3 {
@Test
public void client() {
Socket socket = null;
FileInputStream fis = null;
OutputStream os = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
// 创建socket
// socket需要服务端的ip地址或者域名,以及端口号
// 获取ip,定义端口号
InetAddress localHost = InetAddress.getLocalHost();
// InetAddress localHost = InetAddress.getByName('127.0.0.1');
int port = 9090;
socket = new Socket(localHost, port);
// 创建File的实例,FileInputStream的实例
File file = new File("E://mycodes//ideaProject//shangguigujavastudy//JavaSECode//chapter16_net//src//test.png");
// 创建输入流
fis = new FileInputStream(file);
// 通过Socket获取输出流
os = socket.getOutputStream();
// 读写数据
byte[] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1) {
// 写数据
os.write(buffer, 0, len);
}
System.out.println("客户端数据发送完毕");
// 接收来自服务端的信息,应该是输入流
// 接收服务端的信息,应该先表示自己不再发送数据了
socket.shutdownOutput();
is = socket.getInputStream();
// 使用ByteArrayOutputStream防止乱码,内部维护了一个byte字节数组
baos = new ByteArrayOutputStream(); // 写到内存里
byte[] buffer1 = new byte[5];
int len1;
while((len1 = is.read(buffer1)) != -1){
baos.write(buffer1,0, len1);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
// 关闭socket和其他流
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void server() {
ServerSocket serverSocket = null;
InputStream is = null;
FileOutputStream fos = null;
OutputStream os = null;
try {
// 创建serverSocket
int port = 9090;
serverSocket = new ServerSocket(port