会员可以在此提问,百战程序员老师有问必答
对大家有帮助的问答会被标记为“推荐”
看完课程过来浏览一下别人提的问题,会帮你学得更全面
截止目前,同学们一共提了 132358个问题
JAVA 全系列/第二阶段:JAVA 基础深化和提高/容器(旧) 3856楼

老师,一对多聊天服务中,我的客户端不是每次都能返回消息。

下面的是服务端的代码

package com.bjsxt.ls.网络编程.TCP.一对多聊天服务器;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;


//创建服务端接收消息的线程
class ChatReceive extends Thread{
    private Socket ss;
    public ChatReceive(Socket n){
        this.ss=n;
    }

    @Override
    public void run() {
        this.receive1();
    }

    //创建接收消息的线程
    public void receive1(){
        BufferedReader br =null;
        try {
            br = new BufferedReader(new InputStreamReader(this.ss.getInputStream()));
            while (true){
                String jieshou = br.readLine();
                synchronized ("同步锁") {       //要让发送消息和接收消息的线程处于同步状态,需要在两个线程上加上同一个名字的对象锁
                    ChatServer.BUF = jieshou; //将接收到的消息保存到缓存区
                    "同步锁".notify();        //当消息存到缓存区之后,唤醒发送消息线程,可以发送消息了
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (this.ss != null){
                try {
                    ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

//创建服务端发送消息的线程类
class ChatSend extends Thread{
    private Socket ss;
    public  ChatSend(Socket n){
        this.ss=n;
    }

    @Override
    public void run() {
        this.send1();
    }

    //创建发送消息的方法
    public void send1(){
        PrintWriter pw = null ;
        try {
            pw = new PrintWriter(this.ss.getOutputStream());
            while (true) {
                synchronized ("同步锁") { //要让发送消息和接收消息的线程处于同步状态,需要在两个线程上加上同一个名字的对象锁
                    "同步锁".wait();  //发送消息的线程应先处于等待状态
                    pw.println(ChatServer.BUF); //将消息从缓存区取出,因为BUF是static的,直接用类名就能调用
                    pw.flush();
                }
            }
        }  catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (pw != null){
                pw.close();
            }
            if (this.ss != null){
                try {
                    this.ss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

//主线程用于启动服务端,客户端可以使用之前的“点对点优化”中的GoodP2P
public class ChatServer {
    //定义一个字符串类型的缓存区
    public static String BUF;
    public static void main(String[] args) {
        System.out.println("正在启动服务器.....");
        System.out.println("<HappyDay>聊天室启动");
        ServerSocket serverSocket = null;
        try {
            serverSocket =new ServerSocket(332);
            while (true){
                Socket sk =serverSocket.accept(); //将启动服务端放在循环中,因为每次发送或接收消息都需要启动服务端
                System.out.println("已经连接到IP地址为["+ InetAddress.getLocalHost() +"]的主机。");
                new ChatReceive(sk).start(); //启动线程
                new ChatSend(sk).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

这个是用的优化的点对点聊天作为客户端的代码

package com.bjsxt.ls.网络编程.TCP.点对点优化;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

//创建发送消息线程
class send1 extends Thread{
    private Socket socket =null;
    private Scanner scanner =null;
    public send1(Socket s1,Scanner s2){
        this.socket =s1;
        this.scanner=s2;
    }

    //创建发送消息的方法
    public void sengmessage(){
        PrintWriter pw =null;
        try{
            pw =new PrintWriter(socket.getOutputStream());
            scanner = new Scanner(System.in);
            while (true){
                String say =scanner.nextLine();
                pw.println(say);
                pw.flush();
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (pw != null){
                pw.close();
            }
            if (scanner != null){
                scanner.close();
            }
            if (socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    @Override
    public void run() {
        this.sengmessage();
    }
}

//创建接收消息的线程
class receive extends Thread{
    private Socket socket =null;
    public receive(Socket x){
        this.socket = x;
    }
    public void receivemessage(){
        BufferedReader br= null;
        try {
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (true){
                String rs = br.readLine();
                System.out.println("收到的消息是:"+rs);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket!= null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    @Override
    public void run() {
        this.receivemessage();
    }
}
public class GoodP2P {
    public static void main(String[] args) {
        Scanner scanner = null;
        ServerSocket serverSocket = null;
        Socket socket = null;
        scanner =new Scanner(System.in);
        System.out.println("请输入:server,<port> 或者:" + "<ip>,<port>");
        String str = scanner.nextLine();
        String arr[] =str.split(",");//以逗号为分割点存入数组

        //判断当输入的server时,启动服务端
        if ("server".equals(arr[0])){
            System.out.println("TCP点对点通信,正在启动服务端,连接端口号["+arr[1]+"]");
            try {
                serverSocket =new ServerSocket(Integer.parseInt(arr[1]));//将字符串强转为整型
                socket = serverSocket.accept();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("连接成功!");
        }
        //当输入的不是server时,即进入客户端
        else {
            try {
                socket = new Socket(arr[0],Integer.parseInt(arr[1]));
                System.out.println("连接成功!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //启动发送消息的线程
        new send1(socket,scanner).start();
        new receive(socket).start();
        if (serverSocket != null){
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

下面的运行的截图:

服务端测试


客户端1



客户端2




客户端3




JAVA 全系列/第二阶段:JAVA 基础深化和提高/网络编程(旧) 3857楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/IO流技术 3858楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/网络编程 3859楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/多线程技术(旧) 3861楼

截屏2021-09-03 下午3.51.45.png

同楼上,一样的报错,不知原因


JAVA 全系列/第二阶段:JAVA 基础深化和提高/IO 流技术(旧) 3862楼

老师我这里为何会报异常,请老师帮忙看看谢谢,学到这,我有点迷糊了,有点听不懂了

package com.bjsxt.server;
import java.util.List;
import java.util.Map;
import org.dom4j.Element;
import com.bjsxt.servelet.Servlet;
/**
 * 编写WebApp类
 * @author ADMIN
 *(1)初始化程序运行的数据
 *(2)根据不同的url创建所请求的Servlet对象
 */
public class WebApp {//App的意思就是应用程序
 private static ServletContext contxt;
 //使用静态块初始化Servlet容器
 static{
  contxt=new ServletContext();
  //分别获取对应关系的Map集合
  Map<String,String>servlet=contxt.getServlet();
  Map<String,String>mapping=contxt.getMapping();
  //创建解析XML文件的对象
  webDom4j web=new webDom4j();
  web.parse(web.getDocument());//解析xml
  //获取解析XML之后的List集合
  List<Entity>entityList=web.getEntityList();
  List<Mapping>mappingList=web.getMappingList();
 
  //将List集合中的数据存储到Map集合中去
  for (Entity entity : entityList) {
   servlet.put(entity.getName(), entity.getClazz());
  }
  //System.out.println(servlet);
  for(Mapping map:mappingList){
   //遍历url-pattern的集合
   List<String>urlPattern=map.getUrlPattern();
   for(String s:urlPattern ){
    mapping.put(s, map.getName());
   }
  
  } 
  //System.out.println(mapping);
 }
 /**
  * 根据url创建不同的Servlet对象
  * @param url
  * @return
  */
 public static Servlet getServlet(String url){
  if(url==null||url.trim().equals("")){
   return null;
  }
  //调用无参构造方法创建Servlet对象
  
  try {
   //如果url正确
   //根据url的key获取Servlet -name的值/log=login, /reg=register
   String servletName=contxt.getMapping().get(url);
   //根据servletName得到对应的servlet-class
   String servletClass=contxt.getServlet().get(servletName);//得到的是一个完整的包名+类名的字符串
   //使用反射创建Servlet对象
   Class<?>clazz=Class.forName(servletClass);
   Servlet servlet = (Servlet) clazz.newInstance();
   return servlet;
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InstantiationException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return null;
 }
 public static void main(String[] args) {
  System.out.println(getServlet("/log"));
 }
}

image.png

JAVA 全系列/第二阶段:JAVA 基础深化和提高/手写服务器项目(旧) 3868楼
JAVA 全系列/第二阶段:JAVA 基础深化和提高/容器 3869楼

class ManTou {
    private int id;

    public ManTou(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }
}

/**
 * 定义缓冲区类
 */
class SyncStack {
    //定义存放馒头的盒子
    private ManTou[] manTous = new ManTou[10];
    //定义操作盒子的索引
    private int index;

    /**
     * 放馒头的方法
     */

    public synchronized void put(ManTou manTou) {
        //判断盒子是否已满
        while (this.index == this.manTous.length) {
            try {
                this.wait();    //wait()必须在synchronized块中调用  wait()执行后 线程会将持有的对象锁释放 并进入阻塞状态 其他需要该对象锁的线程就可以继续运行了
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //唤醒取馒头的线程
        this.notify();      //该方法必须在synchronized块中调用 会唤醒处于等待状态队列中的一个线程
        this.manTous[this.index] = manTou;
        this.index++;
    }

    /**
     * 取馒头的方法
     */
    public synchronized ManTou get() {
        //判断盒子是否已空
        while (this.index == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notify();
        this.index--;
        return this.manTous[this.index];
    }
}

/**
 * 定义生产者线程类
 */
class ShengChan extends Thread {
    private SyncStack syncStack;

    public ShengChan(SyncStack syncStack) {
        this.syncStack = syncStack;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            ManTou manTou = new ManTou(i);
            syncStack.put(manTou);
            System.out.println("生产第"+(i+1)+"个馒头");
        }
    }
}

/**
 * 定义消费者线程类
 */
class Xiaofei extends Thread {
    private SyncStack syncStack;

    public Xiaofei(SyncStack syncStack) {
        this.syncStack = syncStack;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            ManTou manTou = this.syncStack.get();
            System.out.println("消费了" + (i+1) + "个馒头");

        }
    }
}

public class ProduceThread {
    public static void main(String[] args) {
        SyncStack syncStack = new SyncStack();
        new ShengChan(syncStack).start();
        new Xiaofei(syncStack).start();
    }
}

image.png

image.png

image.png

image.png

老师为什么我这里会出现先消费的现象啊? 

JAVA 全系列/第二阶段:JAVA 基础深化和提高/多线程技术(旧) 3870楼

课程分类

百战程序员微信公众号

百战程序员微信小程序

©2014-2025百战汇智(北京)科技有限公司 All Rights Reserved 北京亦庄经济开发区科创十四街 赛蒂国际工业园
网站维护:百战汇智(北京)科技有限公司
京公网安备 11011402011233号    京ICP备18060230号-3    营业执照    经营许可证:京B2-20212637