网络编程
概述
C/S和B/S
网络通信协议
协议:protocol
网络通信协议市要求双方传递数据的计算机必须遵守的,按照对应的网络传输协议,才可以进入数据的交互和传递
常见协议:
-
UDP 面向无连接,不可靠数据传输存在丢包 传输速度快 没有客户端和服务器区别,都可以作为发送端和接收端 使用场景:直播,网络游戏 -
TCP/IP 面向连接 可靠数据传输,稳定 有明确服务器和客户端概念 使用场景:数据下载,文件传输
网络编程要素
-
协议 两个计算机之间数据传输,需要对应的协议来完成 -
IP地址 Internet Protocol Address 当前计算机在网络中的一个地址编号,类似于手机号码 IP地址有IPv4和IPv6两种格式 -
端口号 端口号是当前应用程序在计算机中的一个编号,是计算机明确,当前的数据是给予哪一个程序使用,或者数据从哪一个程序发出 端口号是一个short类型数据,范围0-65535 0-1024属于特定的系统端口号,不能自定义使用
IP类
InetAddress
常用方法:
-
InetAddress getLocalhost(); 获取本机IP地址类对象 -
InetAddress getByName(String str); 根据指定的主机名获取对应的IP地址对象 -
InetAddress[] getAllByName(String str); 获取指定主机名,或者域名对应的所有IP地址类对象
package cn.ocean888;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Demo1 {
public static void main(String[] args) throws UnknownHostException {
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
InetAddress byNameAddress = InetAddress.getByName("秦的机械革命");
System.out.println(byNameAddress);
InetAddress byNameAddress2 = InetAddress.getByName("www.ocean888.cn");
System.out.println(byNameAddress2);
InetAddress[] byNameAddress3 = InetAddress.getAllByName("www.baidu.com");
for (InetAddress inetAddress : byNameAddress3) {
System.out.println(inetAddress);
}
}
}
UDP协议数据传输
用户数据报协议:User Datagram Protocol
数据传递采用数据包方式传递,所有的数据要进行打包操作,并且没有对应的客户端服务器概念,有且只有发送端和接收端
Socket 套接字
数据在需要进行传递操作时,在数据传递的两台计算机当中必须有对应的Socket,这里采用UDP协议,则必须有一个UDP协议的Socket
DatagramSocket();
创建一个发送端UDP协议Socket对象
DatagramSocket(int port);
创建一个接收端UDP协议的Socket对象,这里需要监听指定端口
数据包的打包方法
发送端数据打包方式
DatagramPacket DatagramPacket(byte[] buf, int length, InetAddress address, int port);
/*
buf:需要传递数据的字节数组
length:当前字节数组中数据容量字节数
address:接收端IP地址对象
port:接收端对应的端口号
*/
接收端数据接收方式
需要准备一个空的数据包
DatagramPacket DatagramPacket(byte[] buf, int length);
/*
buf:字节缓冲数组,通常是1024的整数倍
length:当前字节缓冲数组的容量
*/
发送端
步骤:
- 创建UDP服务器对应的发送端Socket
- 准备对应的数据包,需要带有指定数据
- 发送数据 send
- 发送 udp 发送端
package cn.ocean888;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SenderDemo1 {
public static void main(String[] args) throws IOException {
System.out.println("udp发送端");
DatagramSocket socket = new DatagramSocket();
byte[] bytes = "ocean".getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8848);
socket.send(packet);
socket.close();
}
}
接收端
步骤:
- 打开UDP服务,监听指定端口
- 创建新的空数据包
- 通过Socket接受数据 receive
- 关闭 UDP 接收端
package cn.ocean888;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ReceiveDemo1 {
public static void main(String[] args) throws IOException{
System.out.println("接收端启动");
DatagramSocket socket = new DatagramSocket(8848);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
int length = packet.getLength();
System.out.println(new String(buf, 0, length));
socket.close();
}
}
需要先启动接收端,监听端口
udp数据传输丢失问题
- 网络不好,传输不稳定,带宽不够,数据包丢失
- 电脑性能问题
TCP概述
TCP是相对于UDP比较稳定的传输协议,存在三次握手,保证连接状态,同时明确区分客户端和服务端之分
TCP服务中需要服务器端先启动,需要监听指定端口,等待客户端连接
TCP操作:Java中提供了两个Socket
-
服务端Socket java.net.ServerSocket; 创建对应的ServerSocket开启服务器,等待客户端连接 -
客户端Socket java.net.Socket 创建客户端Socket,并且连接服务器,同时将Socket发送给服务器绑定注册
tcp 三次握手四次挥手流程图
Socket客户端
给客户端提供的数据传输符合TCP/IP要求的Socket对象
构造方法Constructor
Socket(String host, int port);
host是服务器IP地址,port对应服务器程序的端口号
成员方法Method
InputStream getInputStream();
获取Socket对象输入字节流,可以从服务器获取对应的数据
InputStream是一个资源,需要在程序退出时关闭
OutputStream getOutputStream();
获取Socket对象输出字节流,可以发送数据到服务器
OutputStream是一个资源,需要在程序退出时关闭
void close();
关闭客户端Socket
void shutdownOutput();
禁止当前Socket发送数据
TCP/IP协议对应的Socket是给予IO流实现的
ServerSocket服务端Socket
在服务器开启Socket服务器
构造方法 Constructor
ServerSocket(int port);
开启ServerSocket服务器,并且明确服务端口
成员方法 Method:
Socket accept();
监听并且连接,得到一个Socket对象,同时该方法是一个阻塞方法,会始终处于一个监听状态
返回的是Socket,即客户端Socket对象,获取到当前Socket对象,相当于获取到客户端连接,同时使用Socket和客户端一致
TCP客户端服务端输入输出关系
服务器代码
流程:
- 创建ServerSocket服务器,同时监听指定端口
- 通过accept方法获取Socket连接,得到客户端Socket对象
- 通过Socket对象,获取InputStream,读取客户端发送数据
- 通过Socket对象,获取OutputStream,发送数据给客户端
- 关闭服务
package cn.ocean888_tcp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws IOException {
System.out.println("服务端启动");
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = inputStream.read(buf);
System.out.println(new String(buf, 0, length));
OutputStream outputStream = socket.getOutputStream();
String str = "ocean";
outputStream.write(str.getBytes());
socket.close();
}
}
客户端代码
流程:
- 创建Socket服务,同时明确连接服务器的IP地址和对应端口号
- 通过Socket对象,获取对应的OutputStream,发送数据给服务器
- 通过Socket对象,获取对应的InputStream,接收服务器发送数据
- 关闭连接
package cn.ocean888_tcp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientDemo {
public static void main(String[] args) throws UnknownHostException, IOException {
System.out.println("客户端启动");
Socket socket = new Socket("169.254.252.105", 8888);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("client connect".getBytes());
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int length = inputStream.read(buf);
System.out.println(new String(buf, 0, length));
socket.close();
}
}
需要首先启动服务端
文件上传实现
客户端
流程:
- 创建对应的文件输入字节流操作,可以使用缓存
- 启动Socket
- 获取Socket输出OutputStream对象,发送数据给服务器
- 边读边发
- 文件读取结束,发送完毕,关闭客户端
package cn.ocean888_tcp;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class FileClient {
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(new File("D:\\JavaCode\\test\\test.jpg")));
Socket socket = new Socket(InetAddress.getLocalHost(), 8848);
OutputStream outputStream = socket.getOutputStream();
int length = -1;
byte[] buf = new byte[1024 * 8];
while ((length = bufferedInputStream.read(buf)) != -1) {
outputStream.write(buf, 0, length);
}
socket.close();
}
}
服务端
流程:
- 开启服务端,创建ServerSocket服务
- 明确文件保存位置,创建对应文件夹的输出缓冲字节流
- 读取数据,写入文件
- 关闭服务端
package cn.ocean888_tcp;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class FileServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8848);
Socket socket = serverSocket.accept();
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("D:\\JavaCode\\test\\copy.jpg")));
InputStream inputStream = socket.getInputStream();
int length = -1;
byte[] buf = new byte[1024 * 8];
while((length = inputStream.read(buf)) != -1) {
bos.write(buf, 0, length);
}
bos.close();
socket.close();
}
}
运行后
多线程版
解决单线程代码问题:
-
保存的文件名是一致的,无法保存多个文件 使用uuid解决 -
服务器代码需要执行多个功能在结束 -
服务端代码不可能只有一个上传文件功能 使用多线程
package cn.ocean888_tcp;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TcpGoodServer {
public static void main(String[] args) throws IOException {
System.out.println("服务器代码启动");
ServerSocket serverSocket = new ServerSocket(8848);
ExecutorService tp = Executors.newFixedThreadPool(5);
while (true) {
Socket socket = serverSocket.accept();
tp.submit(() -> {
try {
System.out.println(Thread.currentThread().getName());
InputStream inputStream = socket.getInputStream();
String fileName = UUID.randomUUID().toString();
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("D:\\JavaCode\\test\\" + fileName + ".jpg")));
int length = -1;
byte[] buf = new byte[1024 * 8];
while ((length = inputStream.read(buf)) != -1) {
bos.write(buf, 0, length);
}
bos.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
}
运行:先启动服务端在启动多个客户端
|