又是帮别人写的,之前有做过,有点经验,稍作修改,有需要的看看
基本交互功能可以直接看:多线程聊天室学习改进
1、前述
1.1 、题目要求
1.实验目的
(1)掌握Java.IO包的用法;
(2)掌握Java.Net包的用法;
(3)掌握一个完整应用程序的设计、实施及调试的方法
2.实验内容
请写一个基于Socket的聊天程序,两人可以加入一个组进行聊天。程序可以是基于Terminal I/O).
3.提示
(1)程序应是基于Socket,其中该程序即可以做服务器也可以做客户端;
(2)设计两个类Server(可在类中含主方法)和Client(可在类中含主方法),并通过命令行参数确定程序的启动参数:
Server port
Client host port
port为端口号(整数1-65535);
host 为主机IP(可为“127.0.0.1”)
程序启动时要判断参数的合法性;
在聊天过程中,任一方输入"bye",则聊天结束,客户端程序退出,但服务器程序应继续运行。具体处理流程如下:
服务器端:
②服务器启动后处于等待状态
② 收到客户端信息后,显示信息-若信息为"bye",则退出本次会话
③ 再次进入待状态,此时等待用户从标准输入设备输入信息
④ 待取用户输入后,将信息发往客户端
⑤ 返回①
客户端
① 首先等待用户从标准输入设备读入一行文字;
② 然后发往服务器;
③ 进入等待状态
④ 收到服务器的回复后-若为"bye",退出本次会话
⑤再转①
4.实验要求:
(1)上机前查找相关资料;
(2)完成程序的设计。
(3)记录程序设计及调式中的所有问题;
(4)实验学时:2
(5)按实验报告格式撰写实验报告;
(6)请准时上交实验报告,过期还再接受补交;
1.2、实验结果
目前我的能力只能做到支持局域网内不同机子的交互,不能尝试外网通信,有大佬的话可以指点一下,谢谢了
我是把程序打包成2个jar文件,效果如下:
图1. 我的电脑
图2.朋友电脑
图三.我的电脑
2、代码区:
2.1、Client类
包含2个输入输出流处理类
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* @author Lenovo
* @date 2020/12/2
*/
public class Client{
String host;
int port;
String name = ""; // 用户名字
public Client(){
host = "localhost";
port = 9999;
int x = (int) (Math.random() * 100);
this.name = "client" + x;
}
public Client(String host,int port){
this.host = host;
this.port = port;
int x = (int) (Math.random() * 100);
this.name = "client" + x;
}
public void RunClient() {
System.out.println("************客户端" + this.name +"*************");
try {
//创建一个Socket对象
Socket client = new Socket(host, port);
// 创建发送的线程类对象
Send send = new Send(client, name);
// 创建接收的线程类对象
Receive receive = new Receive(client);
new Thread(send).start();
new Thread(receive).start();
} catch (Exception e) {
e.printStackTrace();
System.out.println("请检查服务器是否已经开启或网络等环境问题");
}
}
}
class Send implements Runnable{// 实现发送功能
// 从键盘获取数据
private BufferedReader br;
// 发送数据的数据流
DataOutputStream dos ;
private boolean flag = true; //是否正常
String name = ""; // 用户名字
public Send(){
br = new BufferedReader(new InputStreamReader(System.in));
}
public Send(Socket client,String name){
this(); //调用本类无参方法
try{
dos = new DataOutputStream(client.getOutputStream());
this.name = name;
this.send(name+"已经连上服务器了");
} catch (IOException e) {
//e.printStackTrace();
flag = false;
CloseUtil.closeAll(dos,client);
}
}
private String getMessage(){
String str = "";
try{
str = br.readLine();
} catch (IOException e) {
// e.printStackTrace();
flag = false;
CloseUtil.closeAll(br);
}
return str;
}
private void send(String str){
try{
dos.writeUTF(str);
dos.flush();
} catch (IOException e) {
// e.printStackTrace();
flag = false;
CloseUtil.closeAll(dos);
}
}
@Override
public void run() {
String str;
while(flag){
//循环调用发送信息方法
str = getMessage();
this.send(name+":"+str);
if (str.contains("bye") || str.contains("Bye") || str.contains("See you")){
flag = false;
}
}
}
}
class Receive implements Runnable{
//用于接收数据的数据流
private DataInputStream dis ;
private boolean flag =true;
public Receive(Socket client){
try{
dis = new DataInputStream(client.getInputStream());
} catch (IOException e) {
//e.printStackTrace();
flag = false;
CloseUtil.closeAll(dis,client);
}
}
private String getMessage(){
String str ="";
try{
str = dis.readUTF();
} catch (IOException e) {
//e.printStackTrace();
flag = false;
CloseUtil.closeAll(dis);
}
return str;
}
@Override
public void run() {
while(flag){
System.out.println(this.getMessage());
}
}
}
2.2、Server类
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
/**
* @author Lenovo
* @date 2020/12/2
*/
public class Server {
// 创建一个 ServerSocket类对象
ServerSocket server;
private int port;
// 创建就一个集合对象,存储每个连接进来的客户端
public static List<MyChannel>list = new ArrayList<>();
public static List<Print>printList = new ArrayList<>();
public Server(){port = 9999;}
public Server(int port){
this.port = port;
}
public void RunServer() {
System.out.println("正在启动服务器...");
System.out.println("************服务器端*************");
try {
server = new ServerSocket(port);
while (true) {
Socket socket = server.accept();
//创建线程类的对象,每一个用户存入集合
MyChannel channel = new MyChannel(socket);
// 添加到集合中去
list.add(channel);
// 服务器输出
Print p = new Print(socket);
printList.add(p);
//启动线程
new Thread(p).start();
new Thread(channel).start();
}
// 关闭所有流
// CloseUtil.closeAll(dos,dis,socket);
} catch (IOException e) {
// e.printStackTrace();
CloseUtil.closeAll(server);
}
}
public static void main(String[]args) throws IOException {
if (args.length >= 1) {
String portInfo = args[0];
int port = Integer.parseInt(portInfo);
new Server(port).RunServer();
}else {
new Server().RunServer();
}
}
}
// 输出
class Print implements Runnable { // 适合多线程 作用:服务器自己输入信息传递给其他客户端
public static List<Socket> socketList = new ArrayList<Socket>(); // 群聊里中的socket
// 服务器输出
public Scanner input = new Scanner(System.in);
private DataOutputStream dos;
private boolean flag = true;
public Print(Socket socket) {
socketList.add(socket);
}
@Override
public void run() {
while (flag) { // 让服务器持续在线
String msg = input.nextLine();
if(msg == null || msg.equals("") || msg.equals(" ")
|| msg.equals("\t") || msg.equals("\n") || msg.equals("\r") || msg.equals("\r\n")) return;
System.out.println("(你)服务端公布:" + msg);
Iterator<Socket> iterator = socketList.iterator();
try {
while (iterator.hasNext()) {
Socket tmpSocket = iterator.next();
dos = new DataOutputStream(tmpSocket.getOutputStream());
dos.writeUTF("服务端公布:" + msg);
dos.flush();
}
}
catch (IOException e) {
//e.printStackTrace();
flag = false;
CloseUtil.closeAll(dos);
}
}
}
}
2.3、关闭流类
做一个关闭类,方便各个类调用
import java.io.Closeable;
import java.io.IOException;
/**
* @author Lenovo
* @date 2020/12/2
*/
public class CloseUtil {
public static void closeAll(Closeable... able){
for(Closeable c:able){
if(c!=null){
try{
c.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.4、管道类
被服务器Server调用来处理用户应用的输入输出转发等
/**
* @author Lenovo
* @date 2020/12/2
*/
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.List;
/**
* 每个客户端都是一条管道
* 1、输入流
* 2、输出流
* 3、接收数据
* 4、发送数据
*/
public class MyChannel implements Runnable{
private DataOutputStream dos;
private DataInputStream dis;
private boolean flag = true;
Socket client;
public MyChannel(Socket client) {
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
this.client = client;
} catch (IOException e) {
//e.printStackTrace();
flag = false;
CloseUtil.closeAll(dis,dos);
}
}
//接收数据
private String receive(){
String str= "";
try{
str = dis.readUTF();
} catch (IOException e) {
//e.printStackTrace();
flag = false;
CloseUtil.closeAll(dis);
Server.list.remove(this);
}
return str;
}
// 发送
private void send(String str){
if(str!=null && str.length()!=0){
try {
dos.writeUTF(str);
dos.flush();
}
catch (IOException e) {
//e.printStackTrace();
flag = false;
CloseUtil.closeAll(dis);
Server.list.remove(this);
}
}
}
// 转发
private void sendOther() throws IOException {
String str = this.receive();
if(str == null || str.equals("") || str.equals(" ")
|| str.equals("\t") || str.equals("\n") || str.equals("\r") || str.equals("\r\n")) return;
System.out.println(str);
List<MyChannel> list = Server.list;
boolean endState = false;
if (str.contains("bye") || str.contains("Bye") || str.contains("See you")) { //判断是否有退出语句
endState = true;
}
for(MyChannel other:list){
//判断是不是自己
if(other==this){
// continue; //不发给自己
if(endState)
other.send("(Your)"+str+"[Quit Talking]");
else
other.send("(Your)"+str);
}
else {
if(endState)
other.send("(Your friend)"+str+"[Quit Talking]");
else
other.send("(Your friend)"+str);
}
}
if (endState){ //退出
flag = false;
CloseUtil.closeAll(dis,dos);
int index = Server.list.indexOf(this);
Server.printList.remove(index);
Server.list.remove(this);
}
}
@Override
public void run() {
while(flag){
// 调用发送数据的方法
// send(receive());
try {
sendOther();
} catch (IOException e) {
//e.printStackTrace();
flag = false;
CloseUtil.closeAll(dis);
Server.list.remove(this);
}
}
}
}
2.5、客户端调用类(这个可以开多次)
import java.io.IOException;
/**
* @author Lenovo
* @date 2020/12/2
*/
public class StartClient {
public static void main(String[]args) throws IOException {
if (args.length >= 2) {
String host= args[0];
int port= Integer.parseInt(args[1]);
System.out.println(host+" "+port);
new Client( host,port).RunClient();
} else {
new Client().RunClient();
}
}
}