实验3 网络程序设计

又是帮别人写的,之前有做过,有点经验,稍作修改,有需要的看看
基本交互功能可以直接看:多线程聊天室学习改进

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();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广大菜鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值