美文网首页程序员
Java网络编程(2)

Java网络编程(2)

作者: 發財大力 | 来源:发表于2019-08-22 17:57 被阅读0次

接着昨天的发文,在昨天的项目中,存在着许多的问题,今天就来将这些问题给解决了,同时会以本人思路来一步步的写代码:

要实现的功能:

/**
 * 1.每一个客户端一个名称
 * 2.向某一个客户端发起私聊
 * 3.群聊
 *
 * 客户端只能向服务器发文件或者字符
 * 服务器端只能得到客户端发来的数据
 * 必须客户端和服务器有一个规范
 * 客户端的需求可以在发送的字符里面体现
 *
 * 定义规范:
 * 1.登陆      u+ 姓名  u+
 * 2.返回结果  成功 1 ;失败 -1
 * 3.私聊      p+ 姓名♥内容  P+
 * 4.群聊      a+ 聊天内容  a+
 * 使用Map来保存信息:保存对应的名字信息和一个对应的socket,才能调用每个信息
 * Map<String,Socket>
 */

思路:要实现群聊、私聊、并且给每个客户端都取好名字,首先第一步就是要创建几个类,分别是:服务器类(srever)客户端类(Client)用户管理类(userManage)

由于本阶段没有界面操作,要如何判断客户端发起的是群聊还是私聊呢?为了解决这个问题,我们就需要来定义一套规范:
1.当终端接收到以u+开头和结尾的语句时,如 u+ 内容 u+,要将此内容判定为d登陆,从而去执行登陆相对应的代码块;
2.当终端接收到以a+开头和结尾的语句时,如 a+ 内容 a+,要将此内容判定为群聊,从而去执行群聊相对应的代码块;
3.当终端接收到以p+开头和结尾的语句时,如 p+ 内容 p+,要将此内容判定为私聊,从而去执行私聊相对应的代码块;

服务器端

服务器使用多线程,主线程主要就是在不停的接收客户端链接,并且获取每个客户端的socket

public class Server{
    //创建一个manage对象
    public static UserManage manage = new UserManage();

    //主线程只负责来监听客户端链接
    public static void main(String[] args) {
        //创建ServerSocket
        try (ServerSocket ss = new ServerSocket(8989)){
            //监听所有来链接的客户端
            while (true){
                Socket socket = ss.accept();

                //让子线程处理这个socket
                new ServerThread(socket).start();
            }
        } catch (IOException e) {

        }
    }
}

服务器的子线程需要处理的是判断客户端的登陆状态,如果该用户已经存在,就提示用户重新输入,同时还要判断客户端是发起群聊还是私聊具体实现如下:

class ServerThread extends Thread{
    private Socket socket;

    public ServerThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        //登陆 接收客户端发过来的数据

        BufferedReader br = null;
        PrintStream ps = null;
        try {
            //1.获取对应的输入流,接收客户端输入数据
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            //2.获取对应的输出流,向客户端发送数据
            ps = new PrintStream(socket.getOutputStream());
            //2.接收数据
            String line;
            while ((line = br.readLine()) != null){
                //登陆 U+...U+
                if (line.startsWith(ChatProtocol.LOGIN_FLAG)&&line.endsWith(ChatProtocol.LOGIN_FLAG)){
//                    String[] item = line.substring(2).split("u+");
//                    String name = item[0];
                    //获取用户名
                    String name = line.substring(2,line.length()-2);

                    //调用manage方法,判断这个用户名是否已经登陆
                    if (Server.manage.isLonined(name)){
                        //已经登陆
                        //发送结果给客户端
                        ps.println(ChatProtocol.FAILURE);

                    }else {
                        //没有登陆
                        //调用save保存当前的登陆的用户信息
                        Server.manage.save(name, socket);
                        ps.println(ChatProtocol.SUCCESS);
                    }
                }//判断是不是私聊
                else if (line.startsWith(ChatProtocol.PRIVATE_FLAG)&&line.endsWith(ChatProtocol.PRIVATE_FLAG)){
                    //获取信息
                    String msg = line.substring(2,line.length()-2);
                    //int endIndex
                    //分割
                    String[] items = msg.split(ChatProtocol.STLIT_FLAG);
                    //用户
                    String name = items[0];
                    //聊天内容
                    String message = items[1];
                    //通过用户名找到对应的socket
                    Socket desSocket = Server.manage.socketByName(name);

                    PrintStream desPs = new PrintStream(desSocket.getOutputStream());

                    //获取当前用户名称
                    String currentName = Server.manage.nameBySocket(desSocket);
                    desPs.println(currentName+"向你发来私聊:"+message);
                }else {
                    //群聊
                    //处理数据
                    String msg = line.substring(2,line.length()-2);
                    //获取当前用户名称
                    String currentName = Server.manage.nameBySocket(socket);

                    //遍历所有用户消息
                    Collection<Socket> sockets = Server.manage.allUsers();
                    for (Socket s: sockets){
                        //创建输出流
                        PrintStream tempps = new PrintStream(s.getOutputStream());
                        tempps.println(currentName+":"+msg);
                        tempps.close();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //私聊
        //群聊
    }
}

客户端

客户端也采用多线程的模式,这是因为客户端需要接收服务器返回的消息,同时还要向服务器发送数据,多线程主要接收终端输入,发送给服务器端,具体实现如下:

public class Client{
    public static void main(String[] args) {
        BufferedReader br = null;
        PrintStream ps = null;
        BufferedReader brServer = null;
        //链接服务器端
        try (Socket socket = new Socket("127.0.0.1",8989)){
            //登陆
            //接收终端的输入流
            br = new BufferedReader(new InputStreamReader(System.in));
            //发给服务器的输出流
            ps = new PrintStream(socket.getOutputStream());
            //接收服务器端的输入流
            brServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            while (true){
                //接收终端输入信息
                //BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                //String line = br.readLine();
                String line  = JOptionPane.showInputDialog("请输入用户名");
                //拼接好发送
                String loginStr = ChatProtocol.LOGIN_FLAG+line+ChatProtocol.LOGIN_FLAG;

                ps.println(loginStr);

                String result = brServer.readLine();

                //判断结果
                if (result.equals(ChatProtocol.SUCCESS)){
                    System.out.println("登陆成功!");
                    break;
                }else {
                    System.out.println("用户名已经存在,请重新登陆.");
                }
            }
            //登陆成功
            //开启子线程,处理服务器的输入
            new ClientThread(socket).start();

            //接收终端输入,发送给服务器端
            String line;
            while ((line = br.readLine()) != null){
                //发送给服务器
                ps.println(line);
            }
        } catch (IOException e) {
            System.out.println("网络出错,请检查服务器是否开启。");
        }
    }
}

子线程用来处理服务器返回的数据

class ClientThread extends Thread{
    private Socket socket;
    public ClientThread(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        BufferedReader br =null;
        try {
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //接收服务器发过来的消息
            String line = null;
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("网络出错!");
        }finally {
            //释放资源
            try {
                if (br != null){
                    br.close();
                }
                if (socket != null){
                    socket.close();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端管理

package day14;

import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;


/**
 * 管理所有登陆的用户Map<String, Socket>
 * 方法:
 *     判断用户是否登陆
 */

public class UserManage {
    /**
     * 定义一个Map来管理所有链接,保存每一个用户对应的姓名和Socket
     */
    private Map<String, Socket> users = new HashMap<>();

    /**
     * 判断用户是否登陆
     */
    public boolean isLonined(String name){
        //遍历数组
        for (String key:users.keySet()){
            if(key.equals(name)){
                return true;
            }
        }
        return false;
    }
    /**
     * 保存用户信息
     */
    public void save(String name, Socket socket){
        users.put(name, socket);
    }
    /**
     * 通过用户名找到对应的socket
     */
    public Socket socketByName(String name){

        return users.get(name);
    }
    /**
     * 通过socket对象找到对应的名称
     */
    public String nameBySocket(Socket socket){
        for (String key: users.keySet()){
            //取出这个key对应的socket对象
            if (socket == users.get(key)){
                return key;
            }
        }
        return null;
    }
    /**
     * 获取所有人的放回对象
     */
    public Collection<Socket> allUsers(){
        return users.values();
    }
}

接口类---存放定义的规范

package day14;


public interface ChatProtocol {
    //登陆
    String LOGIN_FLAG = "u+";
    //私聊
    String PRIVATE_FLAG = "p+";
    //群聊
    String PUBLIC_FLAG = "a+";
    //分割符
    String STLIT_FLAG = "♥";
    //成功状态
    String SUCCESS = "1";
    String FAILURE = "-1";
}

感悟

一天下来,很累,但是很开心。一个项目看似简单,可是写了两天,这个过程中出现了许多bug,虽然找bug这个过程是痛苦的,但是还是学会了不少,继续努力吧。

相关文章

  • day26

    1:网络编程(理解)## (1)网络编程:用Java语言实现计算机间数据的信息传递和资源共享 (2)网络编程模型 ...

  • Java网络编程(第四版) PDF 超清版

    《Java网络编程(第四版)》是一本关于Java编程相关的电子书资源,介绍了关于Java、网络编程、Java编程方...

  • IO

    java网络编程 阻塞IO NIO 1. java网络编程 基础知识1.ip地址和端口号2.tcp/udp协议3....

  • Java网络编程(2)

    接着昨天的发文,在昨天的项目中,存在着许多的问题,今天就来将这些问题给解决了,同时会以本人思路来一步步的写代码: ...

  • 2018-2019 目标技术栈

    一、java基础 1.Java 集合 2.Java 线程 3.Java io变成 4.Java 网络编程 二、my...

  • Android应用开发:网络编程2

    网络编程 Java基础:网络编程 Uri、URL、UriMatcher、ContentUris详解 Android...

  • Http协议

    网络编程 Java基础:网络编程 Uri、URL、UriMatcher、ContentUris详解 Android...

  • Chapter 12 . 网络编程

    阅读原文 Chapter 12 . 网络编程 12.1网络编程概述 • Java是 Internet ...

  • 《Netty实战》读书笔记01——第一章

    第 1 章 笔记 Java 网络编程 早期Java的网络编程,需要学习很多C语言套接字(Socket)的知识,但...

  • Java NIO

    书本 Netty权威指南netty实战O’Reilly的《Java nio》Unix网络编程 《unix网络编程》...

网友评论

    本文标题:Java网络编程(2)

    本文链接:https://www.haomeiwen.com/subject/oqxosctx.html