Java网络编程
什么是网络编程?
在网络通讯协议下,不同计算机上运行的程序,进行的数据传输。
- 应用场景:即时通信、网游对战、金融证券、国际贸易、邮件等等。
不管是什么场景,都是计算机跟计算机之间通过网络进行数据传输。
- Java 中可以使用 java.net 包下的技术轻松开发出常见的网络应用程序。
常见的软件架构

BS 架构的优缺点
- 不需要开发客户端,只需要页面 + 服务器
- 用户不需要下载,打开浏览器就能使用
- 如果应用过大,用户体验受到影响(缺点)
CS 架构的优缺点
- 画面可以做得非常精美,用户体验好(优点)
- 需要开发客户端,也需要开发服务端
- 用户需要下载和更新的时候太麻烦
网络编程三要素
IP:确定对方电脑在互联网上的地址
- 设备在网络中的地址,是唯一的标识
端口号:确定接收数据的软件
- 应用程序在设备中唯一的标识
协议:确定网络传输的规则
- 数据在网络中传输的规则,常见的协议有UDP、TCP、http、ftp
IP
全称:Internet Protocol,是互联网协议地址,也称 IP 地址。
是分配给上网设备的数字标签。
通俗理解:上网设备在网络中的地址,是唯一的
常见的 IP 分类为:IPv4、IPv6
IPv4
全称:Internet Protocol version 4,互联网通信协议第四版
采用32位地址长度,分成4组

IPv4 的地址分类形式
- 公网地址(万维网使用)和私有地址(局域网使用)
- 192.168.开头的就是私有地址,范围即为192.168.0.0--192.168.255.255,专门为组织机构内部使用,以此节省 IP
特殊 IP 地址
127.0.0.1,也可以是 localhost:是回送地址也称本地回环地址,也称本地 IP,永远只会寻找当前所在本机。
常用的 CMD 命令
- ipconfig:查看本机 IP 地址
- ping:检查网络是否连通
IPv6
全称:Internet Protocol version 6,互联网通信协议第六版。
由于互联网的蓬勃发展,IP 地址的需求量愈来愈大,而 IPv4 的模式下 IP 的总数是有限的。
采用128位地址长度,分成8组

InetAddress 的使用
package com.faxont.inetaddress;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressDemo1 {
public static void main(String[] args) throws UnknownHostException {
/*
static InetAddress getByName(String host) 确定主机名称的 IP 地址,主机名称可以是机器名称,也可以是 IP 地址
String getHostName() 获取此 IP 地址的主机名
String getHostAddress() 返回文本显示中的 IP 地址字符串
*/
// 1. 获取 InetAddress 的对象
// IP 的对象,也可以看着一台电脑的对象
InetAddress address = InetAddress.getByName("faxont");
System.out.println(address);
System.out.println(address.getHostName());
System.out.println(address.getHostAddress());
}
}
端口号
应用程序在设备中唯一的标识
端口号:由两个字节表示的整数,取值范围:0~65535
其中0~1023之间的端口号用于一些知名的网络服务或者应用
我们自己使用1024以上的端口号就可以了
注意:一个端口号只能被一个应用程序使用
协议
计算机网络中,连接和通信的规则被称为网络通信协议
- OSI 参考模型:世界互联协议标准,全球通信规范,单模型过于理想化,未能在因特网上进行广泛推广
- TCP / IP 参考模型(或TCP / IP 协议):事实上的国际标准

UDP 协议
- 用户数据协议(User Datagram Protocol)
- UDP 是面向无连接通信协议
速度快,有大小限制一次最多发送64K,数据不安全,易丢失数据
UDP 通信程序(发送数据)
- 创建发送端的 DatagramSocket 对象
- 数据打包(DatagramPacket)
- 发送数据
- 释放资源
package com.faxont.udp;
import java.io.IOException;
import java.net.*;
public class SendMessageDemo {
public static void main(String[] args) throws IOException {
// 发送数据
// 1. 创建 DatagramSocket 对象
// 细节:
// 绑定端口,以后我们就是通过这个端口往外发送
// 空参:所有可用的端口中随机一个进行使用
// 有参:指定端口号进行绑定
DatagramSocket ds = new DatagramSocket();
// 2. 打包数据
String str = "你好威啊!!";
byte[] bytes = str.getBytes();
InetAddress address = InetAddress.getByName("127.0.0.1");
int port = 10086;
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, address, port);
// 3. 发送数据
ds.send(dp);
// 4. 释放资源
ds.close();
}
}
UDP 通信程序(接收数据)
- 创建接收端的 DatagramSocket 对象
- 接收打包好的数据
- 解析数据包
- 释放资源
package com.faxont.udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class ReceiveMessageDemo {
public static void main(String[] args) throws IOException {
// 接收数据
// 1. 创建 DatagramSocket 对象
// 细节:
// 在接收的时候,一定要绑定端口
// 而且绑定的端口一定要跟发送的端口保持一致
DatagramSocket ds = new DatagramSocket(10086);
// 2. 创建 DatagramPacket 对象
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
// 3. 调用 DatagramSocket 对象的方法,接收数据
// 细节:
// 该方法是阻塞的
// 程序执行到这一步的时候,会在这里死等
// 等发送端发送数据之后,才会执行下面的代码
System.out.println("等待数据...");
ds.receive(dp);
System.out.println("数据接收完毕...");
// 4. 解析数据包
byte[] data = dp.getData();
int len = dp.getLength();
InetAddress address = dp.getAddress();
int port = dp.getPort();
System.out.println("接收到数据:" + new String(data, 0, len));
System.out.println("来自" + address.getHostAddress() + ":" + port);
// 5. 释放资源
ds.close();
}
}
练习:聊天室
按照下面的要求实现程序
UDP 发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
UDP 接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
package com.faxont.com.practice1;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class ChatRoom {
private static final int BYTES_LENGTH = 1024;
private static final String HOST = "127.0.0.1";
public static void chat(int localPort, int targetPort){
System.out.println("聊天室已启动!");
try{
new Thread(() -> receiveMessage(localPort), "接收线程").start();
sendMessage(targetPort);
}catch (Exception e){
e.printStackTrace();
}
}
private static void sendMessage(int targetPort){
try(DatagramSocket ds = new DatagramSocket()){
Scanner sc = new Scanner(System.in);
while (true) {
String str = sc.next();
byte[] bytes = str.getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName(HOST), targetPort);
ds.send(dp);
System.out.println("发送了:" + str);
if (str.equals("886")) {
break;
}
}
}catch (IOException e){
e.printStackTrace();
}
}
private static void receiveMessage(int localPort){
try(DatagramSocket ds = new DatagramSocket(localPort)){
byte[] bytes = new byte[BYTES_LENGTH];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
while(true){
System.out.println("对方正在输入中...");
ds.receive(dp);
System.out.println("消息加载中...");
byte[] data = dp.getData();
int len = dp.getLength();
InetAddress address = dp.getAddress();
int sendPort = dp.getPort();
String str = new String(data, 0, len);
System.out.println("\n【对方消息】" + str + " —— 来自:" + address.getHostAddress() + ":" + sendPort);
if(str.equals("886")){
System.out.println("对方离开了聊天室...");
break;
}
}
}catch (IOException e){
e.printStackTrace();
}
}
}
package com.faxont.com.practice1;
import java.io.IOException;
public class Chat1 {
public static void main(String[] args) throws IOException {
ChatRoom chatRoom = new ChatRoom();
chatRoom.chat(8888, 9999);
}
}
package com.faxont.com.practice1;
import java.io.IOException;
public class Chat2 {
public static void main(String[] args) throws IOException {
ChatRoom chatRoom = new ChatRoom();
chatRoom.chat(9999, 8888);
}
}
UDP 的三种通信方式(代码实现)
- 单播
之前的代码就是单播
- 组播
组播地址:224.0.0.0 ~ 239.255.255.255
其中224.0.0.0 ~ 224.0.0.255为预留的组播地址
package com.faxont.udp.udpdemo2;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class SendMessageDemo {
public static void main(String[] args) throws IOException {
/*
组播发送端代码
*/
// 创建 MulticastSocket 对象
MulticastSocket ms = new MulticastSocket();
// 创建 DatagramPacket 对象
String s = "你好,你好!";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("224.0.0.1");
int port = 10000;
DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, address, port);
// 调用 MulticastSocket 对象发送数据
ms.send(datagramPacket);
// 释放资源
ms.close();
}
}
package com.faxont.udp.udpdemo2;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class ReceiveMessageDemo1 {
public static void main(String[] args) throws IOException {
/*
组播接收端代码
*/
// 1. 创建 MulticastSocket 对象
MulticastSocket ms = new MulticastSocket(10000);
// 2. 将当前本机添加到224.0.0.1这个组播组中
InetAddress address = InetAddress.getByName("224.0.0.1");
ms.joinGroup(address);
// 3. 创建 DatagramPacket 对象
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
// 4. 接收数据
ms.receive(dp);
// 5. 解析数据
byte[] data = dp.getData();
int len = dp.getLength();
String ip = dp.getAddress().getHostAddress();
String name = dp.getAddress().getHostName();
System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0, len));
// 6. 释放资源
ms.close();
}
}
package com.faxont.udp.udpdemo2;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class ReceiveMessageDemo2 {
public static void main(String[] args) throws IOException {
/*
组播接收端代码
*/
// 1. 创建 MulticastSocket 对象
MulticastSocket ms = new MulticastSocket(10000);
// 2. 将当前本机添加到224.0.0.2这个组播组中
InetAddress address = InetAddress.getByName("224.0.0.2");
ms.joinGroup(address);
// 3. 创建 DatagramPacket 对象
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
// 4. 接收数据
ms.receive(dp);
// 5. 解析数据
byte[] data = dp.getData();
int len = dp.getLength();
String ip = dp.getAddress().getHostAddress();
String name = dp.getAddress().getHostName();
System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0, len));
// 6. 释放资源
ms.close();
}
}
package com.faxont.udp.udpdemo2;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class ReceiveMessageDemo3 {
public static void main(String[] args) throws IOException {
/*
组播接收端代码
*/
// 1. 创建 MulticastSocket 对象
MulticastSocket ms = new MulticastSocket(10000);
// 2. 将当前本机添加到224.0.0.1这个组播组中
InetAddress address = InetAddress.getByName("224.0.0.1");
ms.joinGroup(address);
// 3. 创建 DatagramPacket 对象
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
// 4. 接收数据
ms.receive(dp);
// 5. 解析数据
byte[] data = dp.getData();
int len = dp.getLength();
String ip = dp.getAddress().getHostAddress();
String name = dp.getAddress().getHostName();
System.out.println("ip为:" + ip + ",主机名为:" + name + "的人,发送了数据:" + new String(data, 0, len));
// 6. 释放资源
ms.close();
}
}
- 广播
广播地址:255.255.255.255
TCP 协议
- 传输控制协议 TCP(Transmission Control Protocol)
- TCP 协议是面向连接的通信协议
速度慢,没有大小限制,数据安全
TCP 通信程序
TCP 通信协议是一种可靠的网络协议,它在通信的两端各建立一个 Socket 对象
通信之前要保证连接已经建立
通过 Socket 产生 IO 流来进行网络通信
发送数据
- 创建客户端的 Socket 对象(Socket)与指定服务端连接
Socket(String host, int port)
- 获取输出流,写数据
OutputStream getOutputStream()
- 释放资源
void close
接收数据
- 创建服务器端的 Socket 对象(ServerSocket)
ServerSocket(int port)
- 监听客户端连接,返回一个 Socket 对象
Socket accept()
- 获取输入流,读数据,并把数据显示在控制台
InputStream getInputStream()
- 释放资源
void close()
package com.faxont.tcp.tcpdemo1;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
// TCP 协议,发送数据
// 1. 创建 Socket 对象
// 细节:在创建对象的同时会连接服务端
// 如果连接不上,代码会报错
try(Socket socket = new Socket("127.0.0.1", 10000);
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
){
pw.println("你好你好");
}catch (IOException e){
e.printStackTrace();
}
}
}
package com.faxont.tcp.tcpdemo1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
public class Server {
public static void main(String[] args) {
// TCP 协议,接收数据
// 1. 创建 ServerSocket 对象
try(ServerSocket ss = new ServerSocket(10000);
BufferedReader br = new BufferedReader(new InputStreamReader(ss.accept().getInputStream()))
){
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
练习:TCP 通信练习1 --- 多发多收
客户端:多次发送数据
服务器:多次接收数据,并打印
package com.faxont.com.practice2;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
/*
客户端:多次发送数据
服务器:多次接收数据,并打印
*/
try(Socket socket = new Socket("127.0.0.1", 10000);
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)
){
Scanner sc = new Scanner(System.in);
while(true){
String line = sc.nextLine();
pw.println(line);
if("exit".equals(line)){
break;
}
}
}catch (IOException e){
e.printStackTrace();
}
}
}
package com.faxont.com.practice2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
public class Server {
public static void main(String[] args) {
try(ServerSocket ss = new ServerSocket(10000);
BufferedReader br = new BufferedReader(new InputStreamReader(ss.accept().getInputStream()))
){
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
练习:TCP 通信练习2 --- 接收和反馈
客户端:发送一条数据,接收服务器反馈的消息并打印
服务器:接收数据并打印,再给客户端反馈消息
package com.faxont.com.practice3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
/*
客户端:发送一条数据,接收服务器反馈的消息并打印
服务器:接收数据并打印,再给客户端反馈消息
*/
try(Socket socket = new Socket("127.0.0.1", 10000);
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
){
String str = "你好,服务器!!";
pw.println(str);
socket.shutdownOutput();
try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
}
}catch (IOException e){
e.printStackTrace();
}
}
}
package com.faxont.com.practice3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try(ServerSocket ss = new ServerSocket(10000)){
Socket socket = ss.accept();
try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
String line;
// 细节:
// read() 和 readLine() 方法会从连接通道中读取数据
// 但是,需要有一个结束标记,此处的循环才会停止
// 否则,程序就会一直停在 read() 和 readLine() 方法这里,等待读取下面的数据
while((line = br.readLine()) != null){
System.out.println(line);
}
try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
String str = "你好,客户端!!";
pw.println(str);
}
}
}catch (IOException e){
e.printStackTrace();
}
}
}
练习:TCP 通信练习3 --- 上传文件
客户端:将本地文件上传到服务器,接收服务器的反馈
服务器:接收客户端上传的文件,上传完毕之后给出反馈

package com.faxont.com.practice4;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class Client {
public static void main(String[] args) {
/*
客户端:将本地文件上传到服务器,接收服务器的反馈
服务器:接收客户端上传的文件,上传完毕之后给出反馈
*/
try(Socket socket = new Socket("127.0.0.1", 10000);
FileInputStream fis = new FileInputStream("D:\\java\\workspace\\csb.txt")
){
byte[] bytes = new byte[1024];
int len;
while((len = fis.read(bytes)) != -1){
socket.getOutputStream().write(bytes, 0, len);
}
socket.shutdownOutput();
try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
String msg = br.readLine();
System.out.println("客户端收到服务器反馈:" + msg);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
package com.faxont.com.practice4;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try(ServerSocket ss = new ServerSocket(10000)){
Socket socket = ss.accept();
try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test.txt")){
byte[] bytes = new byte[1024];
int len;
while((len = socket.getInputStream().read(bytes)) != -1){
fos.write(bytes, 0, len);
}
}
try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
pw.println("服务器已收到文件,上传成功!");
}
}catch (IOException e){
e.printStackTrace();
}
}
}
练习:TCP 通信练习4 --- 上传文件(文件名重复问题)
解决上一题文件名重复问题
package com.faxont.com.practice5;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
public class Server {
public static void main(String[] args) {
try(ServerSocket ss = new ServerSocket(10000)){
Socket socket = ss.accept();
// 获取系统时间戳
// String timeStamp = String.valueOf(System.currentTimeMillis());
// try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + timeStamp + ".txt")){
// 利用 UUID 生成文件名
String uuid = UUID.randomUUID().toString().replace("-", "");
try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + uuid + ".txt")){
byte[] bytes = new byte[1024];
int len;
while((len = socket.getInputStream().read(bytes)) != -1){
fos.write(bytes, 0, len);
}
}
try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
pw.println("服务器已收到文件,上传成功!");
}
}catch (IOException e){
e.printStackTrace();
}
}
}
练习:TCP 通信练习5 --- 上传文件(多线程版)
想要服务器不停止,能接收很多用户上传的图片
该怎么做呢?
提示:可以用循环或者多线程
但是仅仅用循环不合理,最优解法是用(循环+多线程)改写
利用 Lambda 表达式 + new Thread
package com.faxont.com.practice6;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) {
try(ServerSocket ss = new ServerSocket(10000)){
System.out.println("服务器接收系统启动");
// 获取系统时间戳
while(true){
Socket socket = ss.accept();
System.out.println("新客户端连接:" + socket.getInetAddress().getHostAddress());
new Thread(() -> {
try(socket){
String timeStamp = String.valueOf(System.currentTimeMillis());
try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + timeStamp + ".txt")){
byte[] bytes = new byte[1024];
int len;
while((len = socket.getInputStream().read(bytes)) != -1){
fos.write(bytes, 0, len);
}
System.out.println("文件上传成功!");
}
try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
pw.println("服务器已收到文件,上传成功!");
}
}catch (IOException e){
e.printStackTrace();
}
}).start();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
利用 Runnable
package com.faxont.com.practice6;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.UUID;
/**
* Record 是什么?
* Java 16 正式出的新语法,专门用来做「纯数据类」。
* 什么叫纯数据类?
* 只存属性
* 不写复杂业务逻辑
* 不需要写构造器、getter、equals、hashCode、toString
* Java 自动帮你全部生成
*
* 特点:
* 1. 所有成员变量默认都是 final
* 2. 不能改值,天然不可变。
* 3. 只能在括号里定义属性,不能再随便加普通成员变量
* 4. 不能显式继承其他类,默认继承 java.lang.Record
* 5. 适合:做实体、做参数载体、临时存数据
* 不适合:复杂业务类、需要增删改成员变量的类
*/
public record MyRunnable(Socket socket) implements Runnable{
@Override
public void run() {
try(socket){
while(true){
// 获取系统时间戳
// String timeStamp = String.valueOf(System.currentTimeMillis());
// try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + timeStamp + ".txt")){
// 利用 UUID 生成文件名
String uuid = UUID.randomUUID().toString().replace("-", "");
try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + uuid + ".txt")){
byte[] bytes = new byte[1024];
int len;
while((len = socket.getInputStream().read(bytes)) != -1){
fos.write(bytes, 0, len);
}
}
try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
pw.println("服务器已收到文件,上传成功!");
}
}
}catch (IOException e){
e.printStackTrace();
}
}
}
package com.faxont.com.practice6;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerRunnable {
public static void main(String[] args) {
try(ServerSocket ss = new ServerSocket(10000)){
System.out.println("服务器接收系统启动");
while(true){
Socket socket = ss.accept();
new Thread(new MyRunnable(socket)).start();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
练习:TCP 通信练习6 --- 上传文件(线程池优化)
频繁创建线程并销毁非常浪费系统资源,所以需要用线程池优化
package com.faxont.com.practice7;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
public class Server {
public static void main(String[] args) {
try(ServerSocket ss = new ServerSocket(10000)){
System.out.println("服务器接收系统启动");
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
3,// 核心线程数
16,// 线程池总大小
60,// 空闲时间(值)
TimeUnit.SECONDS,// 空闲时间(单位)
new ArrayBlockingQueue<>(2),// 队列
Executors.defaultThreadFactory(),// 线程工厂
new ThreadPoolExecutor.AbortPolicy()// 阻塞队列
);
// 获取系统时间戳
while(true){
Socket socket = ss.accept();
System.out.println("新客户端连接:" + socket.getInetAddress().getHostAddress());
threadPool.submit(() -> {
try(socket){
String timeStamp = String.valueOf(System.currentTimeMillis());
try(FileOutputStream fos = new FileOutputStream("D:\\java\\workspace\\test_" + timeStamp + ".txt")){
byte[] bytes = new byte[1024];
int len;
while((len = socket.getInputStream().read(bytes)) != -1){
fos.write(bytes, 0, len);
}
System.out.println("文件上传成功!");
}
try(PrintWriter pw = new PrintWriter(socket.getOutputStream(), true)){
pw.println("服务器已收到文件,上传成功!");
}
}catch (IOException e){
e.printStackTrace();
}
});
}
}catch (IOException e){
e.printStackTrace();
}
}
}
练习:TCP 通信练习7 --- BS(接收浏览器的消息并打印)
客户端:不需要写
服务器:接收数据并打印
package com.faxont.com.practice8;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
public class Server {
public static void main(String[] args) {
try(ServerSocket ss = new ServerSocket(10000);
BufferedReader br = new BufferedReader(new InputStreamReader(ss.accept().getInputStream()))
){
String line;
while((line = br.readLine()) != null){
System.out.println(line);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
练习:TCP 通信练习8 --- 聊天室优化
控制台版的聊天室
package com.faxont.com.practice9;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Server {
public static List<Socket> socketList = Collections.synchronizedList(new ArrayList<>());
public static void main(String[] args) {
try(ServerSocket ss = new ServerSocket(10000)){
while(true){
Socket socket = ss.accept();
System.out.println("有新用户连接:" + socket.getInetAddress().getHostAddress());
socketList.add(socket);
System.out.println("当前在线人数:" + socketList.size());
new Thread(new ClientHandler(socket)).start();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
class ClientHandler implements Runnable{
private final Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try(BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))){
String msg;
while((msg = br.readLine()) != null){
System.out.println("收到:" + msg);
sendAll(msg);
}
}catch (IOException e){
e.printStackTrace();
}
}
private void sendAll(String msg){
synchronized (Server.socketList){
for (Socket socket : Server.socketList) {
try{
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
pw.println(msg);
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
package com.faxont.com.practice9;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
private static final Scanner SC = new Scanner(System.in);
public void start(){
Socket socket = null;
try{
socket = new Socket("127.0.0.1", 10000);
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
new Thread(() -> {
String msg;
try{
while((msg = br.readLine()) != null){
System.out.println("收到:" + msg);
}
}catch (IOException e){
e.printStackTrace();
}
}).start();
String line;
while(!"886".equals(line = SC.nextLine())){
pw.println(line);
}
}catch (IOException e){
e.printStackTrace();
}finally {
if(socket != null){
try{
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
package com.faxont.com.practice9;
import java.io.*;
import java.util.Scanner;
public class Choose {
private static final Scanner SC = new Scanner(System.in);
private static final File SRC_FILE = new File("soketnet\\userInfo.txt");
public void choose(){
System.out.println("欢迎来到聊天室系统!");
while(true){
System.out.println("请选择你要进行的操作:");
System.out.println("1. 登录");
System.out.println("2. 注册");
System.out.println("3. 退出");
int choose = SC.nextInt();
switch (choose){
case 1-> loginCase();
case 2 -> registerCase();
case 3 -> {
System.out.println("退出聊天室系统!");
System.exit(0);
}
default -> System.out.println("输入错误!请重新输入!");
}
}
}
private void loginCase(){
System.out.println("请输入用户名:");
String username = SC.next();
System.out.println("请输入密码:");
String password = SC.next();
if(login(username, password)){
System.out.println("登录成功!用户" + username + "欢迎您!");
new Client().start();
}else{
System.out.println("登录失败!");
}
}
private void registerCase(){
System.out.println("请输入用户名:");
String username = SC.next();
System.out.println("请输入密码:");
String password = SC.next();
if(username.matches("^[a-zA-Z0-9][a-zA-Z0-9_]{3,15}$") && password.matches("^(?=.*[A-Z])[a-zA-Z0-9][a-zA-Z0-9_]{3,15}$")){
if(!isExist(SRC_FILE, username)){
register(username, password);
System.out.println("注册成功!用户" + username + "欢迎您!");
}else{
System.out.println("用户名已存在!请重新输入!");
}
}else{
System.out.println("用户名或密码格式错误!请重新输入!");
}
}
private boolean login(String username, String password){
try(BufferedReader userReader = new BufferedReader(new FileReader(SRC_FILE))){
String line;
while((line = userReader.readLine()) != null){
String[] userInfo = parseLoginData(line);
if(username.equals(userInfo[0]) && password.equals(userInfo[1])){
return true;
}
}
}catch (IOException e){
e.printStackTrace();
}
return false;
}
/**
* 解析登录字符串:username=zhangsan&password=123456
* 返回 String[0] = 用户名
* 返回 String[1] = 密码
*/
private String[] parseLoginData(String data) {
// 1. 用 & 分割
String[] parts = data.split("&");
// parts[0] = "username=zhangsan"
// parts[1] = "password=123456"
String username = null;
String password = null;
// 2. 遍历分割后的每一段,再用 = 分割
for (String part : parts) {
String[] keyValue = part.split("=");
// keyValue[0] = 键(username/password)
// keyValue[1] = 值
if (keyValue[0].equals("username")) {
username = keyValue[1];
} else if (keyValue[0].equals("password")) {
password = keyValue[1];
}
}
// 返回 用户名、密码
return new String[]{username, password};
}
private void register(String username, String password){
try(BufferedWriter userWriter = new BufferedWriter(new FileWriter(SRC_FILE, true))){
userWriter.write("username=" + username + "&password=" + password);
userWriter.newLine();
userWriter.flush();
}catch (IOException e){
e.printStackTrace();
}
}
private boolean isExist(File srcFile, String username){
try(BufferedReader userReader = new BufferedReader(new FileReader(srcFile))){
String line;
while((line = userReader.readLine()) != null){
String[] userInfo = parseLoginData(line);
if(username.equals(userInfo[0])){
return true;
}
}
}catch (IOException e){
e.printStackTrace();
}
return false;
}
}
package com.faxont.com.practice9;
public class Test {
public static void main(String[] args) {
Choose choose = new Choose();
choose.choose();
}
}

浙公网安备 33010602011771号