网络编程意义
网络编程使得不同计算机之间能够进行通信和交互,从而实现了许多现代应用程序和服务的功能。
常见的网络编程架构
CS(客户端服务端模式):需要客户端开发
BS(浏览器服务端模式):不需要开发客户端
IP
设备的唯一标识符
端口号
应用程序在设备中的唯一标识符 ,每个端口号都是设备的出口或入口,所有设备在网络中都是通过端口进行传输接收数据
协议
UDP协议
面向无连接,速度快,有大小限制,数据不安全,数据易丢失
传输接收数据4步走
java
//创建主机对象
//调用方法创建主机对象
InetAddress address = InetAddress.getByName("小胖子");
System.out.println(address);
//获取主机名字
String name = address.getHostName();
System.out.println(name);
//获取主机地址
String hostAddress = address.getHostAddress();
System.out.println(hostAddress);
java
public static void main(String[] args) throws IOException {
//利用UDP端口发送数据
/*
* 1.创建发送数据的平台
* 2.打包数据
* 3,发送
* 4,释放资源
* */
/*
* 传输:
* 若无指定参数则默认随机传递端口
* 若指定参数则传递到指定端口位置
* */
//1. 创建发送数据的平台
DatagramSocket socket = new DatagramSocket();
//要传输的内容
String str = "丢那醒啊";
//转换为字节数据传递数据
byte[] bytes = str.getBytes();
//要发送到的目的地
//创建主机对象,要发送的目的地·
InetAddress address = InetAddress.getByName("小胖子");
//创建端口
int port = 10086;
//2. 打包要发送的数据
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);
//3. 发送内容
socket.send(dp);
//4. 释放资源
socket.close();
}
java
public static void main(String[] args) throws IOException {
/*
* 接收数据:
* 指定接收数据平台
* 接收数据包
* */
//创建接收数据平台
DatagramSocket socket = new DatagramSocket(10086);
//1. 创建接收数据包对象
//存放接收的字节数据进数组
byte[] bytes = new byte[1024];
//2. 创建接收数据包
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
//调用接收数据方法Receive,此时接收的数据存入数据包packet中
socket.receive(packet);
//3. 接收数据
//从哪个设备中发出
InetAddress address = packet.getAddress();
//发出的数据内容以字节数组进行接收
byte[] data = packet.getData();
//从哪个设备的端口中发出
int port = packet.getPort();
String str = new String(data, 0, data.length);
System.out.println("接收的数据内容为:" + str);
System.out.println("发送数据的电脑端口为:" + port);
//4. 释放资源
socket.close();
}
单播、组播、广播
单播:一台设备向另一台设备发送数据
组播:一台设备向另一组设备发送数据(如:机房授课,授课设备向一组被授课设备发送信息)(组播地址:224.0.0.0 ~ 239.255.255.255其中预留组播地址:224.0.0.0 ~ 224.0.0.255)
广播:一台设备向所有设备同时发送数据,所有设备都能接收到 (广播地址:255.255.255.255)
java
public class MultSend {
//组播发送数据,一台设备向一组设备同时发送数据,要保证组播地址完全一致
public static void main(String[] args) throws IOException {
///1. 创建组播对象(要发送的平台)
MulticastSocket ms = new MulticastSocket();
//2. 打包数据包(要发送的数据内容)
String str = "吃蒙你啊";
byte[] bytes = str.getBytes();
//接收的地址
InetAddress address = InetAddress.getByName("224.0.0.1");
//发送端口号
int port = 10086;
//打包成数据包
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address, port);
//3. 发送数据包
ms.send(packet);
//4. 释放资源
ms.close();
}
}
public class MulReceive2 {
public static void main(String[] args) throws IOException {
//接收组播发送的数据
//1. 创建组播平台(组播的端口号必须保持一致)
MulticastSocket ms = new MulticastSocket(10086);
//2. 接收组播数据包
//用多大空间接收
byte[] bytes = new byte[1024];
//将本机添加到组播中
InetAddress address = InetAddress.getByName("224.0.0.1");
ms.joinGroup(address);
//接收包的端口号
int port = 10086;
//创建接收包对象
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, address, 10086);
//3. 接收数据包
ms.receive(packet);
//解析数据
InetAddress source = packet.getAddress();
byte[] data = packet.getData();
int length = packet.getLength();
int packetPort = packet.getPort();
String str = new String(data, 0, length);
//4. 释放资源
ms.close();
System.out.println("地址为" + source + "从端口" + packetPort + "发送了:" + str);
}
}
TCP协议
面向有连接即需要两端必须连接,否则无法传输数据 ,传输速度快,数据不易丢失
三次握手四次挥手协议三次握手:客户端向服务端请求发送数据(第一次握手),服务端回应客户端表示收到请求(第二次握手),客户端确认将数据发送(第三次握手)
四次挥手:客户端向服务端请求停止发送数据(第一次挥手),服务端回应客户端表示收到该请求 (第二次挥手),但此时服务端可能还未完全接收完整数据,若此时立刻关闭传输通道则数据丢失,故要等待服务端发送确认停止传输数据(第三次挥手),客户端确认停止发送数据(第四次挥手)
java
public class Cilen {
public static void main(String[] args) throws IOException {
/*
* UDP协议只管发送数据,不管通道是否连接,故容易发生丢失数据的情况
* TCP协议要保证两台设备直接已经构建通道,不然无法传输数据
* */
//获取本机对象IP地址
InetAddress address = InetAddress.getByName("小胖子");
String hostAddress = address.getHostAddress();
System.out.println(hostAddress);
//创建Socket对象
//若获取不到服务端对象会报错
Socket socket = new Socket(address, 10086);
//写出数据
OutputStream os = socket.getOutputStream();
//创建字符输出流
os.write("吃蒙你啊啊!666".getBytes());
//释放资源
os.close();
socket.close();
}
}
public class Server{
public static void main(String[] args) throws IOException {
//创建服务器对象
ServerSocket serverSocket = new ServerSocket(10086);
//和客户端连接(获取到连接的客户端对象)
Socket s = serverSocket.accept();
/*InputStream is = s.getInputStream();
InputStreamReader isr = new InputStreamReader(is);*/
//字符缓冲流读取数据
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
int b;
while ((b = br.read()) != -1) {
System.out.print((char) b);
}
br.close();
s.close();
serverSocket.close();
}
}
test:客户端和服务端互发消息
java
public class client {
//客户端发送一条数据,接收服务器的反馈并打印
public static void main(String[] args) throws IOException {
//1.创建客户端对象
/*
* 参数一:信息要发送的目的地,此时发送到本机
* 参数二:发送端绑定的端口
* */
Socket socket = new Socket(InetAddress.getByName("小胖子"), 10086);
//2.发送数据
String str;
//创建键盘录入对象
Scanner sc = new Scanner(System.in);
//键盘输入内容
System.out.println("请输入要发送的内容:");
str = sc.nextLine();
//3.输出
OutputStream os = socket.getOutputStream();
os.write(str.getBytes());
//细节:关闭通道,让程序继续往下读
socket.shutdownOutput();
//4.接收反馈消息
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
int b;
while ((b = isr.read()) != -1) {
System.out.print((char) b);
}
//5.关闭资源
socket.close();
}
}
public class Server {
public static void main(String[] args) throws IOException {
//服务端,接收客户端的消息并打印,再给客户端反馈消息
//1.创建客户端对象(绑定对应端口)
ServerSocket ss = new ServerSocket(10086);
//2.获取客户端对象(等待客户端连接,连接成功将返回连接对象)
Socket socket = ss.accept();
//3.获取字节输入流对象
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
//读入数据
/*
* 细节:
* 从通道中读入数据时若没有读取到结束标记程序会卡在此处
* 若要程序继续往下运行需要关闭通道
* */
int b;
while ((b = isr.read()) != -1) {
System.out.print((char) b);
}
//4. 反馈消息
OutputStream os = socket.getOutputStream();
String str = "接收成功";
os.write(str.getBytes());
//5.释放资源
socket.close();
ss.close();
}
}
控制台版聊天室练习
客户端
java
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws IOException {
//客户端
while (true) {
//创建客户端对象,连接服务端
Socket socket = new Socket(InetAddress.getByName("小胖子"), 10086);
System.out.println("连接服务器");
Scanner sc = new Scanner(System.in);
//登录界面
ChatJFrame();
System.out.println("请输入你的选择:");
switch (sc.nextLine()) {
case "1" -> Login(socket);
default -> System.out.println("输入错误,请重新输入");
}
}
}
//登录
public static void Login(Socket socket) throws IOException {
/*
* 其他要求:
用户名和密码要求:
要求1:用户名要唯一,长度:6~18位,纯字母,不能有数字或其他符号。
要求2:密码长度3~8位。第一位必须是小写或者大小的字母,后面必须是纯数字。
* */
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//拼接输入内容,并传递给服务端
StringBuilder sb = new StringBuilder();
String str = sb.append(name + "=").append(password).toString();
sendContent(socket, str);
//接收服务器反馈
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String outcome = br.readLine();
//根据反馈做出不同的相应
/*
"用户名不符合要求"
"密码不符合要求"
"没有该用户信息"
"登录成功"
"用户名和密码不匹配"
*/
if ("登录成功".equals(outcome)) {
System.out.println("登录成功,开始聊天");
startChat(socket);
} else {
System.out.println(outcome);
}
}
private static void startChat(Socket socket) throws IOException {
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入你要发送的内容:");
String str = sc.nextLine();
sendContent(socket, str);
}
}
private static void sendContent(Socket socket, String str) throws IOException {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(str);
bw.newLine();
bw.flush();
}
//登录界面
public static void ChatJFrame() {
System.out.println("==============欢迎来到食懵尼啊聊天室================");
System.out.println("1登录");
System.out.println("2注册");
}
}
服务端
java
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Server {
public static void main(String[] args) throws IOException {
//创建服务端对象
ServerSocket ss = new ServerSocket(10086);
while (true) {
//获取客户端对象(与谁连接)
Socket socket = ss.accept();
System.out.println("成功连接客户端");
myRunnable myRunnable = new myRunnable(socket);
//开启多条线程,同时连接多个客户端
Thread thread = new Thread(myRunnable);
thread.start();
}
}
private static void login(Socket socket) throws IOException {
//将客户端写出的数据读入
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str = br.readLine();
String[] split = str.split("=");
String name = split[0];
String password = split[1];
//获取服务端输出对象,回写数据给客户端
//判断用户登录的账户密码信息
//1.判断用户名是否符合要求
boolean flag = isName(name);
if (!flag) {
//回写数据
System.out.println("用户名不符合要求");
loginOutCome(socket, "用户名不符合要求");
return;
}
//2.判断密码是否符合要求
flag = isPassword(password);
if (!flag) {
System.out.println("密码不符合要求");
loginOutCome(socket, "密码不符合要求");
return;
}
//3.校验用户密码符合要求后遍历文件内的内容是否含有该用户信息
//创建输入流对象(将文件内容导入程序)
br = new BufferedReader(new InputStreamReader(new FileInputStream("Work/register")));
//创建双列集合存储用户名和密码
HashMap<String, String> hm = new HashMap<>();
//将文件内容加载入程序
while ((str = br.readLine()) != null) {
hm.put(str.split("=")[0], str.split("=")[1]);
}
//判断是否含有该信息
boolean b = hm.containsKey(name);
if (!b) {
System.out.println("没有该用户信息");
loginOutCome(socket, "没有该用户信息");
return;
}
String value = hm.get(name);
if (value.equals(password)) {
System.out.println("登录成功");
loginOutCome(socket, "登录成功");
while (true) {
receiveContent(socket, name);
}
} else {
System.out.println("用户名和密码不匹配");
loginOutCome(socket, "用户名和密码不匹配");
}
}
private static void receiveContent(Socket socket, String name) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str = br.readLine();
System.out.println(name + ":" + str);
//转发消息给其他用户
}
private static void loginOutCome(Socket socket, String outcome) throws IOException {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(outcome);
bw.newLine();
bw.flush();
}
private static boolean isPassword(String password) {
//要求2:密码长度3~8位。第一位必须是小写或者大小的字母,后面必须是纯数字。
Pattern p = Pattern.compile("[0-9]{3,8}");
Matcher m = p.matcher(password);
return m.matches();
}
private static boolean isName(String name) {
// 要求1:用户名要唯一,长度:6~18位,纯字母,不能有数字或其他符号。
Pattern p = Pattern.compile("[a-zA-z]{2,18}");
Matcher m = p.matcher(name);
return m.matches();
}
static class myRunnable implements Runnable {
Socket socket;
myRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
login(this.socket);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}