TCP协议,一个服务器(ServerSocket)只服务于一个客户端(Socket),那么可以通过ServerSocket+Thread的方式,实现一个服务器服务于多个客户端。

多线程服务器实现原理——多线程并发机制
1、创建服务器ServerSocket,开启监听.accept(),当有客户端连接,返回一个Socket对象。
2、把Socket对象需要处理的事务,交给Thread,此线程会到一边默默地执行,那么服务器监听就会空闲出来,等待另外一个客户端连接。
3、另一个客户端连接成功,新的Socket对象又交给另一个Thread,以此类推。

【多线程聊天室---群聊】先运行服务器,在运行客户端(2个客户端则运行两次)

                               

  1. import java.io.*;
  2. import java.net.ServerSocket;
  3. import java.net.Socket;
  4. import java.util.HashSet;
  5.  
  6. //获取客户端发来的消息,并把消息发送到所有客户端
  7. public class Server {
  8. private HashSet<Socket> allSocket;//保存所有服务器的套接字集合
  9. private ServerSocket server;//服务器套接字
  10.  
  11. public Server() {//构造方法
  12. try {
  13. server = new ServerSocket(4567);//实例化服务器套接字,设定端口4567
  14. } catch (IOException e) {
  15. e.printStackTrace();
  16. }
  17. allSocket = new HashSet<>();//实例化服务器套接字集合
  18. }
  19.  
  20. public void startServer() {//启动服务器,循环获取服务器监听
  21. while (true) {
  22. Socket socket = null;
  23. try {
  24. socket = server.accept();//服务器监听是否有客户端接入
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. System.out.println("用户进入聊天室");//监听成功(客户端连接成功),输出此句代码
  29. allSocket.add(socket);//连接成功的客户端放入集合中
  30. ServerThread t = new ServerThread(socket);//将成功连接的客户端交给线程
  31. t.start();
  32. }
  33. }
  34.  
  35. private class ServerThread extends Thread {//发送一个客户端信息给所有客户端
  36. Socket socket;//客户端套接字
  37.  
  38. public ServerThread(Socket socket) {//带参数(客户端套接字)的构造方法
  39. this.socket = socket;//参数赋值给客户端套接字
  40. }
  41.  
  42. public void run() {//发送一个客户端信息给所有客户端
  43. BufferedReader br = null;//读取客户端发来的消息
  44. try {
  45. br = new BufferedReader(new InputStreamReader(socket.getInputStream()));//获得套接字的字节流
  46. while (true) {//把客户端发来的消息,发给所有客户端
  47. String str = br.readLine();//读取字符串
  48. //若字符串中有EXIT内容(有用户退出聊天室),提醒其他人
  49. if (str.contains("EXIT")) {//用户退出聊天室,给服务器发送"%EXIT%:用户名"(自定义规则)
  50. String tmp = str.split(":")[1] + "用户退出聊天室";//依据:分隔成两个元素,取第2个元素(用户名)
  51. sendMessageToAllClient(tmp);//告诉所有人,他退出了
  52. allSocket.remove(socket);//移除离开的客户端
  53. socket.close();//关闭客户端
  54. return;//停止线程
  55. }
  56. sendMessageToAllClient(str);//如果没有EXIT内容,直接发送给所有客户端。调用自定义方法
  57. }
  58. } catch (IOException e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. }
  63.  
  64. private void sendMessageToAllClient(String str) {//发给所有人,参数str是发送的内容
  65. for (Socket s : allSocket) {//遍历所有接入的客户端
  66. try {
  67. BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));//客户端输出流
  68. bw.write(str);//输出str
  69. bw.newLine();//换行
  70. bw.flush();//强制将输出流缓冲区的数据送出,清空缓冲区(一般用在IO中)
  71. } catch (IOException e) {
  72. e.printStackTrace();
  73. }
  74. }
  75. }
  76.  
  77. public static void main(String[] args) {
  78. Server s = new Server();
  79. s.startServer();
  80. }
  81. }
  1. import javax.swing.*;
  2. import javax.swing.border.EmptyBorder;
  3. import java.awt.*;
  4. import java.awt.event.ActionEvent;
  5. import java.awt.event.ActionListener;
  6. //登陆界面、获取用户名与IP
  7. public class LinkServerFrame extends JFrame {
  8. private JPanel contentPane;
  9. private JLabel lblIP;
  10. private JLabel lblUserName;
  11. private JTextField tfIP;
  12. private JTextField tfUserName;
  13. private JButton btnLink;
  14.  
  15. public LinkServerFrame(){//构造方法
  16. setTitle("连接服务器");
  17. setResizable(false);
  18. setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  19. setBounds(100,100,390,150);
  20.  
  21. contentPane=new JPanel();
  22. contentPane.setBorder(new EmptyBorder(5,5,5,5));
  23. contentPane.setLayout(null);
  24. setContentPane(contentPane);
  25.  
  26. lblIP=new JLabel("服务器IP地址:");
  27. lblIP.setFont(new Font("微软雅黑",Font.PLAIN,14));
  28. lblIP.setBounds(20,15,100,15);
  29. contentPane.add(lblIP);
  30.  
  31. tfIP=new JTextField("127.0.0.1");//指定IP内容,本地服务器
  32. tfIP.setBounds(121,13,242,21);
  33. contentPane.add(tfIP);
  34. tfIP.setColumns(10);
  35.  
  36. lblUserName=new JLabel("用户名");
  37. lblUserName.setFont(new Font("微软雅黑",Font.PLAIN,14));
  38. lblUserName.setBounds(60,40,60,15);
  39. contentPane.add(lblUserName);
  40.  
  41. tfUserName=new JTextField();
  42. tfUserName.setBounds(121,42,242,21);
  43. contentPane.add(tfUserName);
  44. tfUserName.setColumns(10);
  45.  
  46. btnLink=new JButton("连接服务器");//登录按钮
  47. btnLink.addActionListener(new ActionListener() {//实现登录,显示客户端窗体,关闭登录窗体
  48. public void actionPerformed(ActionEvent e) {
  49. do_btnLink_actionPerformed(e);
  50. }
  51. });
  52. btnLink.setFont(new Font("微软雅黑",Font.PLAIN,14));
  53. btnLink.setBounds(140,80,120,23);
  54. contentPane.add(btnLink);
  55. }
  56.  
  57. public static void main(String[] args) {
  58. LinkServerFrame linkServerFrame=new LinkServerFrame();//创建窗体对象
  59. linkServerFrame.setVisible(true);//显示窗体
  60. }
  61.  
  62. protected void do_btnLink_actionPerformed(ActionEvent e){
  63. if(!tfIP.getText().equals("")&&!tfUserName.getText().equals("")){//文本框内容不为空
  64. dispose();//关闭窗体,释放资源
  65. //获取文本并除去空格,.trim()的作用是:去掉字符串左右的空格
  66. ClientFrame clientFrame=new ClientFrame(tfIP.getText().trim(),tfUserName.getText().trim());//实例化登录窗口,并赋值IP、用户名
  67. clientFrame.setVisible(true);//显示客户端窗体
  68. }else {
  69. JOptionPane.showMessageDialog(null,"内容不能为空!","警告",JOptionPane.WARNING_MESSAGE);
  70. }
  71. }
  72. }
  1. import javax.swing.*;
  2. import javax.swing.border.EmptyBorder;
  3. import java.awt.event.*;
  4. import java.text.SimpleDateFormat;
  5. import java.util.Date;
  6. //客户端窗体、共享消息在文本域中、用户退出提醒
  7. public class ClientFrame extends JFrame {
  8. private JPanel contentPane;
  9. private JLabel lblUserName;//显示用户名
  10. private JTextField tfMessage;//信息输入文本框
  11. private JButton btnSend;//发送按钮
  12. private JTextArea textArea;//信息接收文本域
  13. private String userName;//用户名称
  14. Client client;//调用自定义类(在Client.java中)
  15.  
  16. public ClientFrame(String ip,String userName){//ip与userName由登录窗体获得(LinkServerFrame.java中)
  17. this.userName=userName;
  18. init();//窗体初始化,自定义方法
  19. addListener();//调用发送按钮监听,自定义方法
  20.  
  21. client=new Client(ip,4567);//赋值形参。实例化自定义类(在Client.java中)
  22. ReadMessageThread t=new ReadMessageThread();//显示消息在文本域中
  23. t.start();
  24. }
  25.  
  26. private void init(){//窗体属性(显示与否,由LinkServerFrame.java控制)
  27. setTitle("客户端");
  28. setResizable(false);
  29. setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
  30. setBounds(100,100,450,300);
  31.  
  32. contentPane=new JPanel();
  33. contentPane.setBorder(new EmptyBorder(5,5,5,5));
  34. contentPane.setLayout(null);
  35. setContentPane(contentPane);
  36.  
  37. JScrollPane scrollPane=new JScrollPane();
  38. scrollPane.setBounds(5,5,434,229);
  39. contentPane.add(scrollPane);
  40.  
  41. textArea=new JTextArea();
  42. //添加滚动条,或JScrollPane scrollPane=new JScrollPane(textArea);
  43. scrollPane.setViewportView(textArea);
  44. textArea.setEditable(false);//文本域不可编辑
  45.  
  46. JPanel panel=new JPanel();
  47. panel.setBounds(5,235,434,32);
  48. contentPane.add(panel);
  49. panel.setLayout(null);
  50.  
  51. lblUserName=new JLabel(userName);
  52. lblUserName.setHorizontalAlignment(SwingConstants.TRAILING);
  53. lblUserName.setBounds(2,4,55,22);
  54. panel.add(lblUserName);
  55.  
  56. tfMessage=new JTextField();
  57. tfMessage.setBounds(62,5,274,22);
  58. tfMessage.setColumns(10);
  59. panel.add(tfMessage);
  60.  
  61. btnSend=new JButton("发送");
  62. btnSend.setBounds(336,4,93,23);
  63. panel.add(btnSend);
  64.  
  65. tfMessage.validate();//验证容器中的组件,即刷新。
  66. }
  67. //显示消息在文本域中
  68. private class ReadMessageThread extends Thread{//内部类,收消息线程类
  69. public void run() {
  70. while (true) {
  71. String str=client.receiveMessage();//调用自定义方法(在Client.java中)
  72. textArea.append(str+"\n");//将消息显示在文本域中
  73. }
  74. }
  75. }
  76.  
  77. private void addListener(){
  78. btnSend.addActionListener(new ActionListener() {//发送按钮的动作监听
  79. public void actionPerformed(ActionEvent e) {
  80. Date date=new Date();
  81. SimpleDateFormat sf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
  82. client.sendMessage(userName+" "+sf.format(date)+":\n"+tfMessage.getText());
  83. tfMessage.setText("");//消息发送后,文本框清空
  84. }
  85. });
  86. //开启窗口监听,单击窗口“X”,弹出确认对话框。“是”则关闭客户端窗体
  87. this.addWindowListener(new WindowAdapter() {
  88. public void windowClosing(WindowEvent atg0){//窗口关闭时
  89. int op=JOptionPane.showConfirmDialog(ClientFrame.this,"确定要退出聊天室吗?","确定",JOptionPane.YES_NO_OPTION);
  90. if(op==JOptionPane.YES_OPTION){//如果选择“是”
  91. client.sendMessage("%EXIT%:"+userName);//如果退出,发送"%EXIT%:用户名"给服务器
  92. try {
  93. Thread.sleep(200);//200ms后,关闭客户端
  94. } catch (InterruptedException e) {
  95. e.printStackTrace();
  96. }
  97. client.close();//关闭客户端套接字,调用Client类中的close方法(Client.java中)
  98. System.exit(0);//关闭程序
  99. }
  100. }
  101. });
  102. }
  103. }
  1. import java.io.*;
  2. import java.net.Socket;
  3. //供ClientFrame.java调用的各种方法,消息输入、输出,关闭套接字
  4. public class Client {
  5. Socket socket;//客户端套接字
  6. BufferedWriter bw;//发数据
  7. BufferedReader br;//收数据
  8.  
  9. public Client(String ip,int port){//带参数构造方法,参数值由ClientFrame.java赋予
  10. try {
  11. socket=new Socket(ip,port);//实例化客户端套接字,连接服务器
  12. bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//客户端输出流,输出到服务器
  13. br=new BufferedReader(new InputStreamReader(socket.getInputStream()));//客户端输入流,服务器反馈给客户端
  14. } catch (IOException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18.  
  19. public void sendMessage(String message){//发消息
  20. try {
  21. bw.write(message);//发消息
  22. bw.newLine();//换行
  23. bw.flush();//强制将输出流缓冲区的数据送出,清空缓冲区(一般用在IO中)
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }
  27. }
  28.  
  29. public String receiveMessage(){//收消息
  30. String message=null;
  31. try {
  32. message=br.readLine();//收消息
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. return message;//返回消息结果
  37. }
  38.  
  39. public void close(){//关闭客户端套接字
  40. try {
  41. socket.close();
  42. } catch (IOException e) {
  43. e.printStackTrace();
  44. }
  45. }
  46. }

TCP多线程聊天室的更多相关文章

  1. Linux以下基于TCP多线程聊天室(server)

    接上篇博文,本文是server端的实现,主要实现的功能,就是现实client的连接.转发client发送的消息.以及client掉线提示等功能,同一时候能够在这这上面扩展和TCP以及线程相关的功能木块 ...

  2. Linux以下基于TCP多线程聊天室(client)

    不怎么会弄这个博客的排版,就直接将代码附上: 主要是使用多线程去等待接受数据和发送数据.以下是client的代码: tcpsed.h文件 1 2 3 4 5 6 7 8 9 10 11 12 13 1 ...

  3. Linux下c语言TCP多线程聊天室

    开发环境:Linux,GCC 相关知识:TCP(博客:传送门),线程 附加:项目可能还有写不足之处,有些bug没调出来(如:对在线人数的控制),希望大佬赐教. 那么话不多说,放码过来: 码云:传送门, ...

  4. 基于Linux的TCP网络聊天室

    1.实验项目名称:基于Linux的TCP网络聊天室 2.实验目的:通过TCP完成多用户群聊和私聊功能. 3.实验过程: 通过socket建立用户连接并传送用户输入的信息,分别来写客户端和服务器端,利用 ...

  5. 【C++】基于socket的多线程聊天室(控制台版)

    以前学习socket网络编程和多线程编程的时候写的一个练手程序 聊天室基本功能: 1.用户管理:登录,注册,登出,修改用户名,修改密码 2.聊天室功能:群聊,私聊,获取在线用户列表,获取所有用户列表 ...

  6. 网络编程TCP协议-聊天室

    网络编程TCP协议-聊天室(客户端与服务端的交互); <span style="font-size:18px;">1.客户端发数据到服务端.</span> ...

  7. Linux下c++11多线程聊天室

    刚看的c++11多线程,写个聊天室试试编译的时候加上 c++11 和 多线程库g++ -Wall -std=c++0x -pthread -o server server.cppserver 和cli ...

  8. Java 网络编程 -- 基于TCP 实现聊天室 群聊 私聊

    分析: 聊天室需要多个客户端和一个服务端. 服务端负责转发消息. 客户端可以发送消息.接收消息. 消息分类: 群聊消息:发送除自己外所有人 私聊消息:只发送@的人 系统消息:根据情况分只发送个人和其他 ...

  9. 三种TCP协议聊天室实现

    一 概述 使用Java的IO实现聊天室 使用Java的NIO实现聊天室 使用Netty实现聊天室 二 IO聊天室 1 服务器 public class IOServer { public static ...

随机推荐

  1. SQL约束(主键约束、外键约束、自动递增、不允许空值、值唯一、值默认、值限制范围)

    NOT NULL 不允许空值约束 NOT NULL 约束强制列不接受 NULL 值(NULL值就是没有值或缺值).NOT NULL 约束强制字段始终包含值,即不向字段添加值,就无法插入新记录或者更新记 ...

  2. 常用Mac快捷键

    1.复制Cmd + C 粘贴Cmd + C —-> Cmd + V 剪切Cmd + C —-> Cmd + Opt + V 2.查看隐藏文件 Cmd + shift + . 3. 货币符号 ...

  3. 【python练习题】程序10

    #10.题目:暂停一秒输出,并格式化当前时间. import time print(time.time())#timestamp print(time.localtime(time.time()))# ...

  4. fftshift

    说明:本文为转载http://blog.csdn.net/myathappy/article/details/51344618 Matlab fftshift 详解 一.实信号情况 因为实信号以fs为 ...

  5. java中的几个概念

    1.JDK(Java Development Kit ):编写Java程序的程序员使用的软件(它是编写java程序,使用到的工具包,为程序员提供一些已经封装好的 java 类库) 2.JRE(Java ...

  6. Ubuntu16.04安装使用wps

    Ubuntu16.04安装使用wps 1.wps官网下载并安装wps 此处以Debian安装包为例,官网下载路径 http://www.wps.cn/product/wpslinux/# 直接安装: ...

  7. wpgwhpg

    //f[i][j]就是第is时wpgwhpg的疲劳度是j,那么我们就可以就ta这1s是否休息进行讨论 #include<bits/stdc++.h> using namespace std ...

  8. 清北澡堂 Day2 上午 一些比较重要的关于数论的知识整理

    1.算数基本定理: 对于任意的大于1的正整数N,N一定能够分解成有限个质数的乘积,即 其中P1<P2<...<Pk,a1,a2,...,ak>=1; 证: 存在性: 若存在最小 ...

  9. 基于网络编程 TCP协议 及 socket 基本语法

    socket是什么 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面, ...

  10. Escape HDU - 3605(归类建边)

    Escape Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Subm ...