文章目录
- 第16章_网络编程拓展练习
- TCP编程
- 1、学生与老师交互
- 2、查询单词
- 3、拓展:查询单词
- 4、图片上传
- 5、拓展:图片上传
- 6、多个客户端上传文件
- 7、群聊
- UDP编程
- 8、群发消息
第16章_网络编程拓展练习
TCP编程
1、学生与老师交互
案例:客户端模拟学生咨询,服务器端模拟咨询老师,进行交互。
客户端收到信息:
欢迎咨询尚硅谷!
这个月的所有期班都已经满了,只能报下一个月的了!
服务器端收到信息:
你好,我想报名这个月的JavaEE就业班!
好的,赶紧给我占个座!
提示:
(1)如果是一个客户端与服务器端交互,怎么实现
(2)如果是多个客户端与服务器交互,怎么实现
package com.atguigu.exercise1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class Exercise1Client {
public static void main(String[] args) {
Socket socket = null; // 创建Socket指定ip地址和端口号
PrintStream ps = null;
try {
socket = new Socket("127.0.0.1", 8888);
// 获取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(br.readLine());
// 获取输出流
ps = new PrintStream(socket.getOutputStream());
ps.println("你好,我想报名这个月的JavaEE就业班!");
System.out.println(br.readLine());
ps.println("好的,赶紧给我占个座!");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ps != null)
ps.close();
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(1)服务端接收1个客户端访问
package com.atguigu.exercise1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.time.LocalDateTime;
public class Exercise1Server {
public static void main(String[] args) {
ServerSocket server = null;
Socket socket = null;
try {
server = new ServerSocket(8888);
socket = server.accept();
// 获取输出流
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("欢迎咨询尚硅谷!");
// 获取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(socket.getInetAddress().getHostAddress() + "留言:" + LocalDateTime.now());
System.out.println(br.readLine() + "\n");
ps.println("这个月的所有期班都已经满了,只能报下一个月的了!");
System.out.println(socket.getInetAddress().getHostAddress() + "留言:" + LocalDateTime.now());
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (server != null)
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(2)服务端接收多个客户端访问
package com.atguigu.exercise1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.time.LocalDateTime;
public class Exercise1 {
public static void main(String[] args) {
ServerSocket server = null;
try {
server = new ServerSocket(8888);
boolean flag = true;
while (flag) {
Socket socket = server.accept();
new Thread() {
public void run() {
try {
// 获取输出流
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("欢迎咨询尚硅谷");
// 获取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(socket.getInetAddress().getHostAddress() + "留言:" + LocalDateTime.now());
System.out.println(br.readLine() + "\n");
ps.println("这个月的所有期班都已经满了,只能报下一个月的了!");
System.out.println(socket.getInetAddress().getHostAddress() + "留言:" + LocalDateTime.now());
System.out.println(br.readLine());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (server != null)
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2、查询单词
案例:模拟客户端输入要查询的中文,服务器返回对应的英文单词
效果如下:
开发提示:
(1)服务器端有一个map,key是中文词语,value是对应的单词
(2)服务器接收到客户端的词语后,从map中get,如果可以找到,就返回单词,如果找不到,就返回“没有找到”
package com.atguigu.exercise2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
public class Exercise2Server {
public static void main(String[] args) {
ServerSocket server = null;
Socket socket = null;
try {
HashMap<String, String> dictionary = new HashMap<String, String>();
dictionary.put("星期一", "Monday");
dictionary.put("星期二", "Tuesday");
dictionary.put("星期三", "Wednesday");
dictionary.put("星期四", "Thursday");
dictionary.put("星期五", "Friday");
dictionary.put("星期六", "Saturday");
dictionary.put("星期七", "Sunday");
//...
server = new ServerSocket(8888);
socket = server.accept();
// 获取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 获取输出流
PrintStream ps = new PrintStream(socket.getOutputStream());
//接收客户端的中文
String key = br.readLine();
//查询对应的英文单词,并返回结果
String words = dictionary.get(key);
if (words != null) {
ps.println(words);
} else {
ps.println("o(╥﹏╥)o没有找到对应的单词!");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (server != null)
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.atguigu.exercise2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Exercise2Client {
public static void main(String[] args) {
Socket socket = null;
Scanner input = null;
try {
// 创建Socket指定ip地址和端口号
socket = new Socket("127.0.0.1", 8888);
input = new Scanner(System.in);
System.out.print("请输入要查询的词语:");
String content = input.next();
// 获取输出流
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println(content);
// 获取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("查询结果:" + br.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
if (input != null)
input.close();
}
}
}
3、拓展:查询单词
修改前一个题目,改为并发版。
服务器端:
package com.atguigu.exercise3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
public class Exercise3Server {
private static HashMap<String, String> dictionary = new HashMap<String, String>();
static {
dictionary.put("星期一", "Monday");
dictionary.put("星期二", "Tuesday");
dictionary.put("星期三", "Wednesday");
dictionary.put("星期四", "Thursday");
dictionary.put("星期五", "Friday");
dictionary.put("星期六", "Saturday");
dictionary.put("星期七", "Sunday");
}
public static void main(String[] args) {
ServerSocket server = null;
try {
server = new ServerSocket(8888);
while (true) {
Socket socket = server.accept();
new QueryThread(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (server != null)
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static class QueryThread extends Thread {
Socket socket;
public QueryThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader br = null;
PrintStream ps = null;
try {
// 获取输入流
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 获取输出流
ps = new PrintStream(socket.getOutputStream());
//接收客户端的中文
String key = br.readLine();
//查询对应的英文单词,并返回结果
String words = dictionary.get(key);
if (words != null) {
ps.println(words);
} else {
ps.println("o(╥﹏╥)o没有找到对应的单词!");
}
} catch (IOException e) {
e.printStackTrace();
;
} finally {
try {
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
if (ps != null)
ps.close();
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端:
package com.atguigu.exercise3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Exercise3Client {
public static void main(String[] args) {
Socket socket = null;
Scanner input = null;
PrintStream ps = null;
BufferedReader br = null;
try {
// 创建Socket指定ip地址和端口号
socket = new Socket("127.0.0.1", 8888);
input = new Scanner(System.in);
System.out.print("请输入要查询的词语:");
String content = input.next();
// 获取输出流
ps = new PrintStream(socket.getOutputStream());
ps.println(content);
// 获取输入流
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("查询结果:" + br.readLine());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ps != null)
ps.close();
try {
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
if (input != null)
input.close();
}
}
}
4、图片上传
案例:客户端给服务器端上传照片
要求:
(1)客户端上传的照片,需要是jpg格式的,并且大小在2M(含)以内的,否则不能上传
(2)要求上传成功后,服务器要返回上传成功,如果上传失败,服务器返回上传失败
(3)客户端上传到服务器端的照片,存储在项目名下"photo"的文件夹中,并且以“照片原文件名+时间戳.jpg”命名
效果如下:
package com.atguigu.exercise4;
import java.io.DataInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Exercise4Server{
public static void main(String[] args) {
ServerSocket server = null;
Socket socket = null;
DataInputStream dis = null;
PrintStream ps = null;
FileOutputStream fos = null;
try {
//开启服务器
server = new ServerSocket(8888);
//接收一个客户端的连接
socket = server.accept();
//获取输入流
dis = new DataInputStream(socket.getInputStream());
//获取输出流
ps = new PrintStream(socket.getOutputStream());
//(1)先读取文件名
String filename = dis.readUTF();
//(2)生成唯一的文件名
String destfile = "photo" + "/" + filename + System.currentTimeMillis() + ".jpg";
//(3)读取文件内容,并写入目标文件
fos = new FileOutputStream(destfile);
try {
byte[] data = new byte[1024];
int len;
while ((len = dis.read(data)) != -1) {
fos.write(data, 0, len);
}
//返回结果给客户端
ps.println("接收成功!");
} catch (Exception e) {
//返回结果给客户端
ps.println("接收失败!");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
ps.close();
try {
if (dis != null)
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (server != null)
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.atguigu.exercise4;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.Scanner;
public class Exercise4Client {
public static void main(String[] args)throws IOException {
//连接服务器
Socket socket= new Socket("127.0.0.1",8888);
//选择要上传的文件
Scanner input = new Scanner(System.in);
System.out.println("请选择要上传的文件:");
//例如:D:\尚硅谷_0325班_柴林燕_JavaSE\笔记\第14章 IO流.docx
String fileStr = input.nextLine();
File file = new File(fileStr);
if(!fileStr.endsWith(".jpg")){
System.out.println("照片必须是.jpg格式");
input.close();
socket.close();
return;
}
if(file.length()>1024*1024*2){
System.out.println("照片必须在2M(含)以内");
input.close();
socket.close();
return;
}
DataOutputStream dos = null;
//从file读取内容,给服务器发送
FileInputStream fis = null;
try {
//获取输出流
dos = new DataOutputStream(socket.getOutputStream());
//先发送文件名
dos.writeUTF(file.getName().substring(0, file.getName().lastIndexOf(".")));
//发送文件内容
fis = new FileInputStream(file);
byte[] data = new byte[1024];
int len;
while((len = fis.read(data)) !=-1){
dos.write(data, 0, len);
}
socket.shutdownOutput();//告诉服务器,我发送完毕
} catch (Exception e) {
System.out.println("上传失败");
}finally{
fis.close();
dos.close();
}
//接收结果
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String result = br.readLine();
System.out.println(result);
br.close();
socket.close();
input.close();
}
}
5、拓展:图片上传
相较于上题,修改为并发版。
服务器端:
package com.atguigu.exercise5;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Exercise5Server {
public static void main(String[] args) {
ServerSocket server = null;
try {
//开启服务器
server = new ServerSocket(8888);
while (true) {
//接收一个客户端的连接
Socket socket = server.accept();
new UploadPhotoThread(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (server != null)
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static class UploadPhotoThread extends Thread {
Socket socket;
public UploadPhotoThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (
//获取输入流
ObjectInputStream dis = new ObjectInputStream(socket.getInputStream());
//获取输出流
PrintStream ps = new PrintStream(socket.getOutputStream());
){
//(1)先读取文件名
String filename = dis.readUTF();
//(2)生成唯一的文件名
String destfile = "photo" + "/" + filename + System.currentTimeMillis() + ".jpg";
//(3)读取文件内容,并写入目标文件
FileOutputStream fos = null;
try {
fos = new FileOutputStream(destfile);
byte[] data = new byte[1024];
int len;
while ((len = dis.read(data)) != -1) {
fos.write(data, 0, len);
}
//返回结果给客户端
ps.println("接收成功!");
} catch (Exception e) {
//返回结果给客户端
ps.println("接收失败!");
} finally {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端:
package com.atguigu.exercise5;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Exercise5Client {
public static void main(String[] args) throws IOException {
//连接服务器
Socket socket = new Socket("127.0.0.1", 8888);
//选择要上传的文件
Scanner input = new Scanner(System.in);
System.out.println("请选择要上传的文件:");
//例如:D:\尚硅谷_0325班_柴林燕_JavaSE\笔记\第14章 IO流.docx
String fileStr = input.nextLine();
File file = new File(fileStr);
if (!fileStr.endsWith(".jpg")) {
System.out.println("照片必须是.jpg格式");
input.close();
socket.close();
return;
}
if (file.length() > 1024 * 1024 * 2) {
System.out.println("照片必须在2M(含)以内");
input.close();
socket.close();
return;
}
//获取输出流
ObjectOutputStream dos = new ObjectOutputStream(socket.getOutputStream());
//先发送文件名
dos.writeUTF(file.getName().substring(0, file.getName().lastIndexOf(".")));
//从file读取内容,给服务器发送
FileInputStream fis = new FileInputStream(file);
//发送文件内容
byte[] data = new byte[1024];
int len;
while ((len = fis.read(data)) != -1) {
dos.write(data, 0, len);
}
socket.shutdownOutput();//告诉服务器,我发送完毕
//接收结果
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String result = br.readLine();
System.out.println(result);
fis.close();
dos.close();
br.close();
socket.close();
input.close();
}
}
6、多个客户端上传文件
需求:每一个客户端启动后都可以给服务器上传一个文件;服务器接收到文件后保存到一个upload目录中,可以同时接收多个客户端的文件上传。
思考分析:
(1)服务器端要“同时”处理多个客户端的请求,那么必须使用多线程,每一个客户端的通信需要单独的线程来处理。
(2)服务器保存上传文件的目录只有一个upload,而每个客户端给服务器发送的文件可能重名,所以需要保证文件名的唯一。我们可以使用“时间戳”作为文件名,而后缀名不变
(3)客户端需要给服务器上传文件名(含后缀名)以及文件内容。而文件名是字符串,文件内容不一定是纯文本的,因此选择ObjectOutputStream 和 ObjectInputStream。
服务器示例代码:
package com.atguigu.exercise6;
import java.net.ServerSocket;
import java.net.Socket;
public class Exercise6Server {
public static void main(String[] args) {
ServerSocket server = null;
try {
//服务器在8888端口号监听数据
server = new ServerSocket(8888);
while (true) {
//(2)等待连接
//这句代码执行一次,意味着一个客户端连接
Socket accept = server.accept();
FileUploadThread ft = new FileUploadThread(accept);
ft.start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (server != null)
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.atguigu.exercise6;
import java.io.*;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileUploadThread extends Thread {
private Socket socket;
private String dir = "upload/";//可以把它放到配置文件中
public FileUploadThread(Socket socket) {
this.socket = socket;
}
public void run() {
FileOutputStream fos = null;
ObjectInputStream dis = null;
PrintStream ps = null;
try {
InputStream is = socket.getInputStream();
dis = new ObjectInputStream(is);
OutputStream out = socket.getOutputStream();
ps = new PrintStream(out);
//读取文件名(含后缀名)
String filename = dis.readUTF();
int lastIndexOfDot = filename.lastIndexOf(".");
//截取后缀名
String ext = filename.substring(lastIndexOfDot);
//生成时间戳
SimpleDateFormat sf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
//拼接新文件名 = 旧文件名 + 时间戳 + 后缀名
String newFilename = filename.substring(0, lastIndexOfDot) + sf.format(new Date()) + ext;
//拼接文件路径
String pathname = dir + File.separator + newFilename;
//用新文件路径构建文件输出流
fos = new FileOutputStream(pathname);
//接收文件内容
byte[] data = new byte[1024];
long sum = 0;
int len;
while ((len = dis.read(data)) != -1) {
fos.write(data, 0, len);
fos.flush();
sum += len;
}
System.out.println("sum = " + sum);
//返回结果
ps.println(filename + "已上传完毕");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (dis != null)
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
if (ps != null)
ps.close();
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端示例代码:
package com.atguigu.exercise6;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class Exercise6Client {
public static void main(String[] args) {
// (1)连接服务器
Socket socket = null;
FileInputStream fis = null;
ObjectOutputStream dos = null;
OutputStream out = null;
Scanner input = null;
BufferedReader br = null;
try {
socket = new Socket("127.0.0.1", 8888);
input = new Scanner(System.in);
out = socket.getOutputStream();
// 用它的目的是为了既可以单独传一个字符串,又可以写字节内容
dos = new ObjectOutputStream(out);
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);// 把字节流转成字符流
br = new BufferedReader(isr);
// (2)从键盘输入文件的路径和名称
System.out.print("请选择要上传的文件:");
String path = input.nextLine();
File file = new File(path);
// 先发送文件名(含后缀名)
dos.writeUTF(file.getName());// 单独发一个字符串
// 还需要一个IO流,从文件读取内容,给服务器发过去
fis = new FileInputStream(file);
// (3)把文件内容给服务器传过去,类似与复制文件
byte[] data = new byte[1024];
int len;
long sum = 0;
while ((len = fis.read(data)) != -1) {
dos.write(data, 0, len);
sum += len;
dos.flush();
}
System.out.println("sum = " + sum);
socket.shutdownOutput();//数据发送完毕,不再发送,但是还要接收,所以是半关闭
// (4)接收服务器返回的结果
String result = br.readLine();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
// (5)关闭
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (dos != null)
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (out != null)
out.close();
} catch (IOException e) {
e.printStackTrace();
}
if (input != null)
input.close();
try {
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
7、群聊
需求:客户端与服务器连接成功后,就可以看到其他客户端的发送的聊天信息,当前客户端也可以发送自己的聊天信息。
思考分析:
(1)服务器
要同时接收多个客户端的连接,因此需要多线程
服务器这边充当转发角色,即在服务器这边的某个客户端的Socket接收到自己客户端发送的消息后,要通过服务器端这边其他客户端的Socket将信息转发出去
(2)客户端
同时能够接收和发送消息,因此也要两个线程,一个接收,一个发送
服务器端示例代码:
package com.atguigu.exercise7;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
public class Exercise7Server {
private static ArrayList<Socket> online = new ArrayList<Socket>();
public static void main(String[] args) {
ServerSocket server = null;
try {
//1、开启服务器
server = new ServerSocket(9999);
while (true) {
//2、接收客户端的连接
Socket socket = server.accept();
//把这个客户端加入到online中
online.add(socket);
//每一个客户端独立的线程
MessageHandler mh = new MessageHandler(socket);
mh.start();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (server != null)
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static class MessageHandler extends Thread {
private Socket socket;
private String ip;
public MessageHandler(Socket socket) {
super();
this.socket = socket;
this.ip = socket.getInetAddress().getHostAddress();
}
public void run() {
BufferedReader br = null;
PrintStream ps = null;
try {
//接收当前的客户端发送的消息
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
br = new BufferedReader(isr);
//这个客户端的一连接成功,线程一启动,就可以告诉其他人我上线了
sendToOthers(ip + "上线了");
while (true) {
String content = br.readLine();
if (content == null) {
break;
}
//收到一句,转发一句
sendToOthers(ip + "说:" + content);
if ("bye".equals(content)) {
//给自己发一句bye
OutputStream out = socket.getOutputStream();
ps = new PrintStream(out);
ps.println("bye");
break;
}
}
sendToOthers(ip + "下线了");
} catch (IOException e) {
sendToOthers(ip + "掉线了");
} finally {
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
if (ps != null)
ps.close();
}
}
//因为转发的代码也很长,独立为一个方法
public void sendToOthers(String str) {
//遍历所有online的客户端
Iterator<Socket> iterator = online.iterator();
while (iterator.hasNext()) {
Socket on = iterator.next();
if (!on.equals(socket)) {//只给其他客户端转发
try {
OutputStream out = on.getOutputStream();
PrintStream ps = new PrintStream(out);
//不能用try(){}catch,因为这里不能关闭流和其他socket
ps.println(str);
} catch (IOException e) {
//说明on这个客户端要么下线了,要么掉线了
iterator.remove();
}
}
}
}
}
}
客户端示例代码:
package com.atguigu.exercise7;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Exercise7Client {
public static void main(String[] args) {
Socket socket = null;
try {
// 1、连接服务器
socket = new Socket("127.0.0.1", 9999);
// 2、开启两个线程,一个收消息,一个发消息
SendThread st = new SendThread(socket);
ReceiveThread rt = new ReceiveThread(socket);
st.start();
rt.start();
// 等发送线程停下来再往下走
st.join();
rt.interrupt();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//关闭socket
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
static class SendThread extends Thread {
private Socket socket;
public SendThread(Socket socket) {
super();
this.socket = socket;
}
public void run() {
Scanner input = null;
PrintStream ps = null;
try {
// 键盘输入
input = new Scanner(System.in);
OutputStream out = socket.getOutputStream();
ps = new PrintStream(out);
while (true) {
// 从键盘输入
System.out.print("请输入要发送的消息:");
String content = input.nextLine();
// 给服务器发送
ps.println(content);
// 如果bye,就结束发送
if ("bye".equals(content)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (input != null)
input.close();
if (ps != null)
ps.close();
}
}
}
static class ReceiveThread extends Thread {
private Socket socket;
public ReceiveThread(Socket socket) {
super();
this.socket = socket;
}
public void run() {
BufferedReader br = null;
try {
InputStream in = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
br = new BufferedReader(isr);
while (true) {
String line = br.readLine();
if ("bye".equals(line)) {
break;
}
System.out.println(line);
}
} catch (SocketException e) {
System.out.println("退出");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
UDP编程
8、群发消息
案例:模拟给全部同学群发“欢迎来到尚硅谷”
开发提示:使用UDP群发
package com.atguigu.exercise8;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Exercise8Send {
public static void main(String[] args) {
DatagramSocket ds = null;
try {
//(1)先建立一个DatagramSocket
ds = new DatagramSocket();
//(2)准备发送的数据
String str = "欢迎来到尚硅谷";
byte[] data = str.getBytes();
for (int i = 0; i <= 255; i++) {
//(3)把数据包装成一个数据报
//DatagramPacket(byte[] buf, int length, InetAddress address, int port)
/*
* 第一个参数:要发送的数据的字节数组
* 第二个参数:数组的长度
* 第三个参数:接收方的IP地址
* 第三个参数:接收方的端口号
*
* 好比发快递,需要填写接收方的IP和端口号
*/
InetAddress ip = InetAddress.getByName("192.168.11." + i);
int port = 8888;
DatagramPacket dp = new DatagramPacket(data, data.length, ip, port);
//(4)发送数据报
// 通过socket发送
ds.send(dp);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//(5)断开
if (ds != null)
ds.close();
}
}
}
package com.atguigu.exercise8;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Exercise8Receiver {
public static void main(String[] args) {
DatagramSocket ds = null;
try {
//1、准备一个socket,用来接收消息
//接收方,先确定端口号,监听数据的端口号
//好比,要收到快递,需要先确定自己的地址和手机号,然后对方才能给你发
ds = new DatagramSocket(8888);
//2、准备一个数据报,来接收数据
//DatagramPacket(byte[] buf, int length)
byte[] data = new byte[1024 * 64];//64K
DatagramPacket dp = new DatagramPacket(data, data.length);
//3、接收数据
ds.receive(dp);
//4、拆包裹
byte[] result = dp.getData();
int len = dp.getLength();//实际接收的数据的长度
System.out.println(new String(result, 0, len));
} catch (IOException e) {
e.printStackTrace();
} finally {
//5、关闭
if (ds != null)
ds.close();
}
}
}