赞
踩
项目名称: JavaChatRoom
项目功能:用户登录、用户下线、群聊、私聊
涉及技术:
1.网络编程(JavaSocket API)
2.Java多线程技术
3.使用C/S架构
项目主体:
服务器:
1.使用ServerSocket来建立起一个连接,public ServerSocket(int port): 此方法默认绑定本地ip地址 127.0.0.1 及指定端口号
2.建立连接accept:
public Socket accept(): 等待客户端连接,线程阻塞,当有客户端连接时,返回客户端socket
3.服务器与客户端通信
当客户端与服务器建立起连接后,通过输入输出流(客户端socket)来进行通信
对于服务器端,获取客户端输入流,读取客户端发来的信息
public InputStream getInputStream()
获取客户端输出流,向客户端发送信息
public OutputStream getOutputStream()
服务器端功能:
注册用户:userName: name
用户群聊:G:聊天内容
用户私聊:P:test-聊天内容
用户退出:byebye
客户端:
1.Socket类:
使用Socket方法: public Socket(String host,int port):绑定指定域名,端口号的服务器,只要不报错就建立连接
2.客户端与服务器通信:
获取客户端输入流,读取服务器发来的信息
public InPutStream getInputStream()
获取客户端输出流,向服务器发送信息
public OutputStream getOutputSteam()
主体代码实现:
分为单线程和多线程版本:
单线程版本:
由于是单线程版本,当没有客户端连接时,服务器线程一直处于阻塞状态,直到有客户端连接时,才会返回客户端socket
服务端代码:
- public class SingleThreadServer {
- public static void main(String[] args) {
- try {
- // 建立服务端ServerScoket 并绑定本地 6666 端口号
- ServerSocket serverSocket = new ServerSocket(6666);
- // 等待客户端连接
- System.out.println("等待客户端连接ing...");
- // 服务器线程一直阻塞,直到有客户端连接,返回客户端连接Socket
- Socket client = serverSocket.accept();
- System.out.println("有客户端连接,客户端端口号为"+client.getPort());
- //获取客户端输出流,向客户端输出信息 自动刷新
- PrintStream out = new PrintStream(client.getOutputStream(),true ,"UTF-8");
- //获取客户端输入流,读取客户端信息
- Scanner in = new Scanner(client.getInputStream());
- if(in.hasNextLine()){
- //读取一整行输入
- System.out.println("客户端发来的信息为:"+in.nextLine());
- }
- out.println("Server : Hello sir, I am Jarvis,may I help you?");
- //关闭流
- in.close();
- out.close();
- serverSocket.close();
- } catch (IOException e) {
- System.err.println("服务器建立连接失败,异常为 "+e);
- }
- }
- }
服务端界面:
客户端代码:
1.客户端建立与服务器的连接,绑定本地ip地址和 指定端口号 6666
2.由于是单线程,服务器是先得到客户端信息,才能向客户端发送消息,为了防止线程阻塞,客户端先向服务器发送信息,再获取服务器端信息
- public class SingleThreadClient {
- public static void main(String[] args) {
- try {
- // 建立客户端Socket并绑定服务器
- Socket client = new Socket("127.0.0.1",6666);
- // 获取输入、输出流与服务器通信
- // 获取输出流,向服务器发送信息
- PrintStream out = new PrintStream(client.getOutputStream(),
- true,"UTF-8");
- out.println("Hi,I am Client");
- //获取输入流,读取服务器发送的信息
- Scanner in = new Scanner(client.getInputStream());
- if(in.hasNextLine()){
- System.out.println("服务器发来的信息为 "+in.nextLine());
- }
- in.close();
- out.close();
- client.close();
- } catch (Exception e) {
- System.err.println("客户端出现异常 :"+e);
- }
- }
- }
客户端界面:
多线程版本:
多线程服务器:
1.使用固定大小线程池实现多线程,这里使用的线程池大小为20
2.在这里使用内部类实现客户端的请求,每当有新的客户端请求,就新建线程处理这个请求
直到线程池满就关闭线程池,关闭服务器
3.在服务器端中,使用concurrentHashMap来存储所有连接到服务器的客户端信息
4.由于每个平台中的换行符不同,识别windows下的换行符,将其替换为 " "
- // 识别windows下换行符,将 \r 替换为 ""
- // pattern为模式,matcher为匹配,匹配后做一个替换
- Pattern pattern = Pattern.compile("\r");
- Matcher matcher = pattern.matcher(str);
- matcher.replaceAll("");
5.四大功能实现
- // 获取客户端输入流,读取客户端发来的信息
- Scanner in = new Scanner(client.getInputStream());
根据这行代码读取客户端发来的信息
- // 一行一行读取客户端信息
- if(in.hasNextLine()){
- str = in.nextLine();
1.注册功能实现
如果识别到 输入信息中有 "userName",就说明有用户注册,使用 split()方法进行字符串分割,将用户名信息分割出来,然后使用 userRegister()方法,将用户信息,及socket保存在 map中,并且打印聊天室中人数。
- if(str.startsWith("userName")){
- // userName:test
- String userName = str.split("\\:")[1];
- userRegister(userName,client);
- continue;
- }
- // 注册方法
- private static void userRegister(String userName,Socket socket){
- System.out.println("用户"+userName+"上线了!");
- // 将用户保存在map中
- clientMap.put(userName,socket);
- System.out.println("当前聊天室中一共有"+clientMap.size()+"人");
- // 告知客户端注册成功
- try {
- // 获取每个Socket的输出流
- PrintStream out = new PrintStream(socket.getOutputStream());
- out.println("用户注册成功!");
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
用户 one two three 都上线,并且聊天室中人数随之更新
2.群聊功能实现
如果识别到输入信息中有 "G",就说明是群聊信息,使用 split() 方法,分割出有用信息
再使用 groupChat(msg) 方法,遍历取出每个soceket,并且获取每个socket的输出流,向每个客户端发送群聊消息
- // 群聊方法
- private static void GroupChat(String msg){
- Set<Map.Entry<String,Socket>> clientSet =
- clientMap.entrySet();
- for(Map.Entry<String,Socket> entry : clientSet){
- // 遍历取出每个Socket
- Socket socket = entry.getValue();
- // 获取每个Socket 的输出流
- try {
- PrintStream out = new PrintStream(socket.getOutputStream());
- out.println("群聊消息为 "+msg);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
3.私聊方法实现
如果输入信息中有" P ",就说明是私聊方法,获取用户名,聊天信息,使用PrivatedChat()方法,传入用户名,信息
然后根据用户名获取指定的socekt,然后获取该socket的输出流,使用 socket.getOutputStream() 向用户发送信息
- // 私聊方法
- private static void PrivateChat(String userName,String msg){
- // 根据用户名获取指定Socket
- Socket socket = clientMap.get(userName);
- try {
- // 获得指定Socket输出流
- PrintStream out = new PrintStream(socket.getOutputStream());
- out.println("私聊消息 "+msg);
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
4.用户退出实现
如果输入的信息中包含 "byebye", 那么遍历map中的key,找到与client相等的 key,移除掉 username ,并同步聊天室中剩余人数
- // 客户端退出
- if(str.contains("byebye")){
- // 遍历Map,获取userName
- String userName = "";
- // 获取所有的方法
- for(String key : clientMap.keySet()){
- // 遍历 Map中的key,找到与client相等的value
- if(clientMap.get(key).equals(client)){
- userName = key;
- }
- }
- System.out.println("用户"+userName+"下线了...");
- clientMap.remove(userName);
- System.out.println("当前聊天室一共 "+clientMap.size()+"人");
- continue;
- }
当有客户端下线时,服务器显示 具体哪个客户端下线,并显示聊天室中还有几人
多线程客户端:
1.socket 绑定服务器 ip 地址及 端口号
2.客户端使用全双工,一个读线程,一个写线程,实现信息的收发
客户端读写线程代码:
-
- public class MultiThreadClient {
-
- public static void main(String[] args) {
- try {
- Socket client = new Socket("127.0.0.1",6666);
- Thread readThread = new Thread(new ReadFromServer(client));
- Thread writeThread = new Thread(new WriteToServer(client));
- readThread.start();
- writeThread.start();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
读线程实现:
获取客户端输入流,使用 Scanner in = new Scanner(client.getInputStream()) 读取服务器发来的信息,
- class ReadFromServer implements Runnable{
- private Socket client;
-
- public ReadFromServer(Socket client){
- this.client = client;
- }
-
- @Override
- public void run() {
- try {
- // 获取客户端输入流,读取服务器发送的信息
- Scanner in = new Scanner(client.getInputStream());
- while(true){
- // 若client已经被关闭,此处自动退出了
- if(in.hasNextLine()){
- System.out.println("从服务器发来的信息为 "+in.nextLine());
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
- }
写线程实现:
// 获取键盘输入流,读取用户从键盘发送的信息
Scanner in = new Scanner(system.in)
// 获取客户端输出流,将用户键入的信息发送给服务器
PrintStream out = new PrintStream(client.getOutputStream())
当用户的输入信息含有 "byebye",关闭键盘输入流,输出流,及客户端
- class WriteToServer implements Runnable{
- private Socket client;
- public WriteToServer(Socket client){
- this.client = client;
- }
-
- @Override
- public void run() {
- try {
- // 获取键盘输入流,读取用户从键盘发来的信息
- Scanner scanner = new Scanner(System.in);
- String string = "";
- // 获取客户端输入流,将用户键入的信息发送给服务器
- PrintStream out = new PrintStream(client.getOutputStream(),
- true,"UTF-8");
- while(true){
- System.out.println("请输入向服务器发送的信息");
- if(scanner.hasNextLine()){
- string = scanner.nextLine();
- out.println(string);
- }
- //设置退出标志
- if(string.contains("byebye")){
- System.out.println("客户端退出,不聊了");
- scanner.close();
- out.close();
- client.close();
- break;
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
客户端注册界面:
客户端one,two,three 都进行注册
客户端群聊界面:
客户端one发起群聊,所有客户端都可以接收到此消息,发送信息为:testtest!
客户端 two 接收到 one发送的消息
客户端 three 接收到 one发送的消息
客户端私聊界面:
three 向 one 送消息
one 接收到消息
由于是私聊,two 没有接收到消息
客户端退出界面:
当 one 发送信息中含有 byebye时,客户端 one 下线
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。