网络编程:就是让两台计算机进行数据交互
网络编程三要素
IP地址:设备在网络中的地址,是唯一的标识
端口:应用程序在设备中的唯一标识
协议:数据在网络中传输的规则,常见的协议有UDP协议和TCP协议
1.IP
IP:全称"互联网协议地址",也称IP地址。是分配给上网设备的数字标签。常见的ip分类为:IPv4和IPv6
IPv4:
IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,而IPv4的模式下IP的总数是有限的。采用128位地址长度,分成8组
特殊情况:如果计算出的16进制表示形式中间有多个连续的0
2.端口
端口:应用程序在设备中的唯一标识
端口号:用两个字节表示的整数,它的取值范围是065535,其中01023之间的端口用于一些知名的网络服务或者应用,我们自己使用1024以上的端口就可以了
一个端口号只能被一个应用程序使用
3.协议
计算机网络中,连接和通信的规则被称为网络通信协议
UDP协议:
TCP协议:
4.UDP发送数据
步骤:
① 创建发送端的DatagramSocket对象
② 创建数据,并把数据打包(DatagramPacket)
③ 调用DatagramSocket对象的方法发送数据
④ 释放资源
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建发送对象
DatagramSocket ds = new DatagramSocket();
//创建数据
String s = "给老村长送礼物";
byte[] bytes = s.getBytes();
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,ip,port);
//发送
ds.send(dp);
ds.close();
}
}
5.UDP接收数据
① 创建接收端的DatagramSocket对象
② 创建一个箱子,用于接收数据
③ 调用DatagramSocket的方法接收数据并将数据放入箱子中
④ 解析数据包,并把数据在控制台显示
⑤ 释放资源
public class ServerDemo {
//注意先启动接收端,再启动发送端
public static void main(String[] args) throws IOException {
//1.创建datagramSocket对象
DatagramSocket ds = new DatagramSocket(10000);
//2.创建一个新的箱子
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//3.接收数据
System.out.println("------------接收前---------");
ds.receive(dp);
System.out.println("------------接收后---------");
//4.从新的箱子获取数据
//byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(bytes,0,length));
ds.close();
}
}
6.UDP的三种通信方式
① 单播
② 组播
③ 广播
之前的实现为单播实现
组播实现:
组播地址:224.0.0.0239.255.255.255,其中224.0.0.0239.0.0.255为预留地址
发送端
public class ClientDemo2 {
public static void main(String[] args) throws IOException {
//创建发送对象
DatagramSocket ds = new DatagramSocket();
//创建数据
String s = "hello组播";
byte[] bytes = s.getBytes();
InetAddress ip = InetAddress.getByName("224.0.1.0");
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,ip ,port);
//发送
ds.send(dp);
ds.close();
}
}
接收端
public class ServerDemo2 {
//注意先启动接收端,再启动发送端
public static void main(String[] args) throws IOException {
//1.创建MulticastSocket对象
MulticastSocket ms = new MulticastSocket(10000);
//2.创建一个新的箱子
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//3.把当前计算机绑定一个组播地址,表示添加到这一组中
ms.joinGroup(InetAddress.getByName("224.0.1.0"));
//4.接收数据
System.out.println("------------接收前---------");
ms.receive(dp);
System.out.println("------------接收后---------");
//5.从新的箱子获取数据
//byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(bytes, 0, length));
ms.close();
}
}
广播实现:
广播地址:255.255.255.255
发送端
public class ClientDemo3 {
public static void main(String[] args) throws IOException {
//创建发送对象
DatagramSocket ds = new DatagramSocket();
//创建数据
String s = "hello 广播";
byte[] bytes = s.getBytes();
InetAddress ip = InetAddress.getByName("255.255.255.255");
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,ip,port);
//发送
ds.send(dp);
ds.close();
}
}
接收端与单播接收端一致
1.TCP通信原理
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,通信之前要保证连接已经建立。
通过Socket产生IO流来进行网络通信
2.TCP发送数据的步骤
① 创建客户端的Socket对象(Socket)与指定服务端连接
Socket(String host,int port)
② 获取输出流,写数据
OutputStream getOutputStream()
③ 释放资源
void close()
public class ClientDemo4 {
public static void main(String[] args) throws IOException {
//创建Socket对象
Socket socket = new Socket("127.0.0.1", 10001);
//获取一个IO流开始写数据
OutputStream os = socket.getOutputStream();
os.write("hello".getBytes());
//释放资源
os.close();
socket.close();
}
}
3.TCP接收数据的步骤
① 创建服务器端的Socket对象(ServerSocket)
ServerSocket(int port)
② 监听客户端连接,返回一个Socket对象
Socket.accept()
③ 获取输入流,读数据,并把数据显示在控制台
InputStream getInputStream()
④ 释放资源
void close()
public class ServerDemo4 {
public static void main(String[] args) throws IOException {
//1.创建客户端连接
ServerSocket serverSocket = new ServerSocket(10001);
//2.等待客户端连接
System.out.println(1111);
Socket accept = serverSocket.accept();
System.out.println(2222);
//3.获取输入流对象
InputStream is = accept.getInputStream();
int len;
while ((len = is.read()) != -1) {
System.out.print((char) len);
}
//4.释放资源
is.close();
serverSocket.close();
}
}
4.细节
① accept()方法是阻塞的,作用等待客户端连接
② 客户端创建对象并连接服务器,此时是通过三次握手协议保证跟服务器之间的连接
③ 针对客户端来讲,是往外写的,所以是输出流,针对服务器来讲,是往里读的,所以是输入流
④ read()方法也是阻塞的
⑤ 在关流的时候,还多了一个往服务器写结束标记的动作
⑥ 最后一步断开连接,通过四次挥手协议保证连接终止
5.三次握手
6.四次挥手
7.TCP通信程序练习
练习1:
① 客户端:发送数据,接收服务器反馈
② 服务器:接收数据,给出反馈
客户端
public class ClientDemo5 {
public static void main(String[] args) throws IOException {
//创建Socket对象
Socket socket = new Socket("127.0.0.1", 10001);
//获取一个IO流开始写数据
OutputStream os = socket.getOutputStream();
os.write("hello".getBytes());
socket.shutdownOutput();
/*InputStream is = socket.getInputStream();
int b;
while ((b=is.read())!=-1){
System.out.println((char) b);
}*/
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
//释放资源
br.close();
os.close();
socket.close();
}
}
服务端
public class ServerDemo5 {
public static void main(String[] args) throws IOException {
//1.创建客户端连接
ServerSocket serverSocket = new ServerSocket(10001);
//2.等待客户端连接
System.out.println(1111);
Socket accept = serverSocket.accept();
System.out.println(2222);
//3.获取输入流对象
InputStream is = accept.getInputStream();
int len;
while ((len = is.read()) != -1) {
System.out.print((char) len);
}
System.out.println("执行了吗");
/* OutputStream os = accept.getOutputStream();
os.write("你谁啊".getBytes());*/
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bw.write("你谁啊");
//4.释放资源
bw.close();
is.close();
accept.close();
serverSocket.close();
}
}
练习2:
① 客户端:将本地文件上传到服务器,接收服务器的反馈
② 服务器:接收客户端上传的文件,上传完毕之后给出反馈
客户端
public class ClientDemo6 {
public static void main(String[] args) throws IOException {
//创建Socket对象
Socket socket = new Socket("127.0.0.1", 10001);
//本地的流,用来读取本地文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("filemodule\\ClientDir\\1.jpg"));
//写到服务器,网络中的流
OutputStream os = socket.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(os);
int b;
while ((b=bis.read())!=-1){
bos.write(b);
}
//给服务器一个结束标记,告诉服务器文件已经传输完毕
socket.shutdownOutput();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while ((line=br.readLine())!=null){
System.out.println(line);
}
//释放资源
bis.close();
socket.close();
}
}
服务端
public class ServerDemo6 {
public static void main(String[] args) throws IOException {
//1.创建客户端连接
ServerSocket serverSocket = new ServerSocket(10001);
while (true) {
//2.等待客户端连接
Socket accept = serverSocket.accept();
//3.网络中的流,从客户端读取数据
BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
//4.本地的IO流,把数据写到本地中,实现永久化存储
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("filemodule\\ServerDir\\"+ UUID.randomUUID().toString()+".jpg"));
int b;
while ((b=bis.read())!=-1){
bos.write(b);
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
//4.释放资源
bos.close();
accept.close();
}
//serverSocket.close();
}
}
多线程优化服务端
① 创建ThreadSocket线程,客户端与之前一致
public class ThreadSocket implements Runnable {
private Socket acceptSocket;
public ThreadSocket(Socket accept) {
this.acceptSocket = accept;
}
@Override
public void run() {
BufferedOutputStream bos = null;
try {
BufferedInputStream bis = new BufferedInputStream(acceptSocket.getInputStream());
//4.本地的IO流,把数据写到本地中,实现永久化存储
bos = new BufferedOutputStream(new FileOutputStream("filemodule\\ServerDir\\" + UUID.randomUUID().toString() + ".jpg"));
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(acceptSocket.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.释放资源
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (acceptSocket != null) {
try {
acceptSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
② 服务端优化
public class ServerDemo6 {
public static void main(String[] args) throws IOException {
//1.创建客户端连接
ServerSocket serverSocket = new ServerSocket(10002);
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3,//核心线程数
10,//线程池的总数量
60,//临时线程空闲时间
TimeUnit.SECONDS,//临时线程空闲时间的单位
new ArrayBlockingQueue<>(5),//阻塞队列
Executors.defaultThreadFactory(),//创建线程的方式
new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略
);
while (true) {
//2.等待客户端连接
Socket accept = serverSocket.accept();
ThreadSocket ts = new ThreadSocket(accept);
//new Thread(ts).start();
pool.submit(ts);
}
}
}