网络通信,IP,TCP/UDP,Socket
1 网络通信的要素
- 通信双方的地址(IP+端口号)
- 网络通信的协议
2 IP:InetAddress
127.0.0.1 : 本机 localhost
java中InetAddress类负责IP地址相关工作
使用InetAddress:
public class Net {
public static void main(String[] args) throws UnknownHostException {
InetAddress byName = InetAddress.getByName("127.0.0.1");
System.out.println(byName);
// 查询百度IP
InetAddress byName1 = InetAddress.getByName("www.baidu.com");
System.out.println(byName1);
InetAddress byName2 = InetAddress.getByName("localhost");
System.out.println(byName2);
InetAddress byName3 = InetAddress.getLocalHost();
System.out.println(byName3);
}
}
3 端口
范围:0~65535
- 公有端口:0 ~1023
HTTP:80
HTTPS:443
FTP:21
Telnet:23 - 程序注册端口:1024~49151
Tomcat:8080
MySQL:3306
Oracle:1521 - 动态端口:49152~65535
4 TCP实现聊天
4.1 客户端
4.2 服务端
5 TCP实现文件上传
项目目录下放置一个文件(图片2)
5.1 客户端
客户端先读文件,形成一个文件流,然后使用这个文件流向socket的输出流写入,最后关闭资源
public class TcpClient {
public static void main(String[] args) throws IOException {
// 创建socket链接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
// 创建输出流
OutputStream outputStream = socket.getOutputStream();
// 读取文件
FileInputStream fileInputStream = new FileInputStream(new File("图片2.png"));
// 写出文件
byte[] bytes = new byte[1024];
int len;
while((len=fileInputStream.read(bytes)) != -1){
outputStream.write(bytes, 0, len);
}
// 关闭资源
fileInputStream.close();
outputStream.close();
socket.close();
}
}
5.2 服务端
创建socket后开始监听,获取到客户端后,从socket获取输入流,从输入流中读取数据,读入文件输出流,保存文件。
public class TcpServer {
public static void main(String[] args) throws IOException {
// 创建服务
ServerSocket serverSocket = new ServerSocket(9000);
// 监听客户端
Socket accept = serverSocket.accept();
// 输入流
InputStream inputStream = accept.getInputStream();
// 文件输出
FileOutputStream receive = new FileOutputStream(new File("receive.png"));
byte[] bytes = new byte[1024];
int len;
while((len=inputStream.read(bytes)) != -1){
receive.write(bytes, 0, len);
}
// 关闭资源
receive.close();
inputStream.close();
accept.close();
serverSocket.close();
}
}
5.3 优化
需要客户端确认服务端接收成功才能断开连接,服务端应该在接收完毕后通知客户端
5.3.1 服务端
public class TcpServer {
public static void main(String[] args) throws IOException {
// 创建服务
ServerSocket serverSocket = new ServerSocket(9000);
// 监听客户端
Socket accept = serverSocket.accept();
// 输入流
InputStream inputStream = accept.getInputStream();
// 文件输出
FileOutputStream receive = new FileOutputStream(new File("receive.png"));
byte[] bytes = new byte[1024];
int len;
while((len=inputStream.read(bytes)) != -1){
receive.write(bytes, 0, len);
}
// 通知客户端
OutputStream outputStream = accept.getOutputStream();
outputStream.write("received".getBytes());
// 关闭资源
receive.close();
inputStream.close();
accept.close();
serverSocket.close();
}
}
5.3.2 客户端
新增:等待服务器通知
public class TcpClient {
public static void main(String[] args) throws IOException {
// 创建socket链接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
// 创建输出流
OutputStream outputStream = socket.getOutputStream();
// 读取文件
FileInputStream fileInputStream = new FileInputStream(new File("图片2.png"));
// 写出文件
byte[] bytes = new byte[1024];
int len;
while((len=fileInputStream.read(bytes)) != -1){
outputStream.write(bytes, 0, len);
}
// 等待服务端通知
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes1 = new byte[1024];
while((len=inputStream.read(bytes1)) != -1){
byteArrayOutputStream.write(bytes1, 0, len);
}
System.out.println(byteArrayOutputStream.toString());
// 关闭资源
fileInputStream.close();
outputStream.close();
socket.close();
}
}
但此时,客户端与服务端均保持运行,卡死
5.4 进一步优化
分析:客户端发送完毕后,立马等待接收,服务端一直在等待接收客户端文件,但服务端不清楚什么时候客户端发送完毕,所以,需要客户端告诉服务端发送文件完毕。
socket.shutdownOutput(),表示客户端输出完毕
客户端代码:
public class TcpClient {
public static void main(String[] args) throws IOException {
// 创建socket链接
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000);
// 创建输出流
OutputStream outputStream = socket.getOutputStream();
// 读取文件
FileInputStream fileInputStream = new FileInputStream(new File("图片2.png"));
// 写出文件
byte[] bytes = new byte[1024];
int len;
while((len=fileInputStream.read(bytes)) != -1){
outputStream.write(bytes, 0, len);
}
// 通知服务端,文件发送完毕
socket.shutdownOutput();
// 等待服务端通知
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes1 = new byte[1024];
while((len=inputStream.read(bytes1)) != -1){
byteArrayOutputStream.write(bytes1, 0, len);
}
System.out.println(byteArrayOutputStream.toString());
// 关闭资源
fileInputStream.close();
outputStream.close();
socket.close();
}
}
6 Tomcat
未来的服务器将使用tomcat不需要自己手动编写服务端
6.1 Tomcat启动
Apache Tomcat寻找对应java版本的Tomcat压缩包解压缩后,去bin目录下找startup.bat文件,运行,启动服务,浏览器访问localhost:8080即可访问tomcat首页
7 UDP
7.1 服务端
public class UdpServer {
public static void main(String[] args) throws IOException {
// 开放端口
DatagramSocket datagramSocket = new DatagramSocket(9090);
// 接收数据
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);
// 阻塞接收信息
datagramSocket.receive(datagramPacket);
// 打印数据
System.out.println(new String(datagramPacket.getData(), 0, datagramPacket.getLength()));
//关闭资源
datagramSocket.close();
}
}
7.2 客户端
public class UdpClient {
public static void main(String[] args) throws IOException {
// 建立socket
DatagramSocket datagramSocket = new DatagramSocket();
// 建立一个包
String msg = "Hi, Server";
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;
DatagramPacket datagramPacket = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port);
// 发送数据包
datagramSocket.send(datagramPacket);
// 关闭资源
datagramSocket.close();
}
}
8 UDP聊天
8.1 服务端
public class UdpChatReceiver {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(6666);
// 准备接收数据
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);
datagramSocket.receive(datagramPacket);
byte[] data = datagramPacket.getData();
String s = new String(data, 0, data.length);
System.out.println(s);
datagramSocket.close();
}
}
8.2 客户端
public class UdpChatSender {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(8888);
// 准备数据
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String s = bufferedReader.readLine();
byte[] bytes = s.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress("localhost", 6666));
datagramSocket.send(datagramPacket);
datagramSocket.close();
}
}
8.3 循环接收-服务端
public class UdpChatReceiver {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(6666);
while (true){
// 准备接收数据
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);
datagramSocket.receive(datagramPacket);
byte[] data = datagramPacket.getData();
String s = new String(data, 0, data.length);
System.out.println(s);
if(s == "bye"){
break;
}
}
datagramSocket.close();
}
}
8.4 循环发送-客户端
public class UdpChatSender {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(8888);
while(true){
// 准备数据
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String s = bufferedReader.readLine();
byte[] bytes = s.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress("localhost", 6666));
datagramSocket.send(datagramPacket);
if(s == "bye"){
break;
}
}
datagramSocket.close();
}
}
9 多线程实现多人UDP聊天
分析:聊天就是一边可以发送同时还能接收,即使用多线程来实现,一个线程负责发送,另一个线程负责接收
9.1 接收数据服务代码
public class TalkReceive implements Runnable{
private DatagramSocket datagramSocket = null;
private int fromPort;
public TalkReceive(int fromPort) throws SocketException {
this.fromPort = fromPort;
this.datagramSocket = new DatagramSocket(fromPort);
}
@Override
public void run() {
while (true){
// 准备接收数据
byte[] bytes = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length);
try {
datagramSocket.receive(datagramPacket);
} catch (IOException e) {
throw new RuntimeException(e);
}
byte[] data = datagramPacket.getData();
String s = new String(data, 0, data.length);
System.out.println("From:" + datagramPacket.getSocketAddress().toString() + ":" + s);
if(s == "bye"){
break;
}
}
datagramSocket.close();
}
9.2 发送数据服务代码
public class TalkSend implements Runnable{
private DatagramSocket datagramSocket = null;
private BufferedReader bufferedReader = null;
private String toIP;
private int toPort;
private int fromPort;
public TalkSend(String toIP, int toPort, int fromPort) throws SocketException {
this.toIP = toIP;
this.toPort = toPort;
this.fromPort = fromPort;
datagramSocket = new DatagramSocket(this.fromPort);
bufferedReader = new BufferedReader(new InputStreamReader(System.in));
}
@Override
public void run() {
while(true){
// 准备数据
String s = null;
try {
s = bufferedReader.readLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
byte[] bytes = s.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length, new InetSocketAddress(this.toIP, this.toPort));
try {
this.datagramSocket.send(datagramPacket);
} catch (IOException e) {
throw new RuntimeException(e);
}
if(s == "bye"){
break;
}
}
datagramSocket.close();
}
}
9.3 学生端
学生端需要实现发送信息和接收信息两个功能,即开启两个线程
public class TalkStudent {
public static void main(String[] args) throws SocketException {
// 开两个线程
new Thread(new TalkSend("localhost", 7777, 8888)).start();
new Thread(new TalkReceive(8889)).start();
}
}
9.4 教师端
教师端开启两个功能
public class TalkTeacher {
public static void main(String[] args) throws SocketException {
new Thread(new TalkSend("localhost", 8889, 7776)).start();
new Thread(new TalkReceive(7777)).start();
}
}
双方端口对齐后双方就可以通信
10 URL
协议:// ip地址:端口 / 项目名 / 资源
测试URL下载
开启tomcat,在tomcat服务器上开设自己的项目与文件
此时,可通过浏览器访问该文件
使用javaURL类实现从服务器下载文件资源
public class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("http://localhost:8080/helloworld/test.txt");
// 连接到这个资源
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream("test.txt");
byte[] bytes = new byte[1024];
int len;
while((len=inputStream.read(bytes)) != -1){
fileOutputStream.write(bytes, 0, len);
}
fileOutputStream.close();
inputStream.close();
urlConnection.disconnect();
}
}
爬取真实网络文件
在网易云上获取到歌曲的url链接
https://m701.music.126.net/20240311213604/a6f286f1de5cbf2ae6b1416d91bd6533/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/34215387885/14a1/817a/88ad/1916048c71eded9536dac87783af9977.m4a
运行程序,获取到.m4a文件即可播放
public class Main {
public static void main(String[] args) throws IOException {
URL url = new URL("https://m701.music.126.net/20240311213604/a6f286f1de5cbf2ae6b1416d91bd6533/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/34215387885/14a1/817a/88ad/1916048c71eded9536dac87783af9977.m4a");
// 连接到这个资源
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream("7.m4a");
byte[] bytes = new byte[1024];
int len;
while((len=inputStream.read(bytes)) != -1){
fileOutputStream.write(bytes, 0, len);
}
fileOutputStream.close();
inputStream.close();
urlConnection.disconnect();
}
}