当前位置:   article > 正文

网络聊天程序的设计与实现——计网课设_基于winsocket的网络群聊程序的设计与实现

基于winsocket的网络群聊程序的设计与实现
  • 实验题目

网络聊天程序的设计与实现

  • 实验目的

使用socket编程,了解socket编程的通信原理,会使用socket进行简单的网络编程,在此基础上编写一聊天程序,能够运行程序,运行客户端和服务器端,实现两个客户端通过服务器端进行通信。

  • 总体设计

1、背景知识

1.1 Tcp协议与Win Sock网络编程接口:

Tcp协议: 是面向连接的运输层协议,提供可靠的、有序的、双向的、面向连接的运输服务。

Win Sock编程: 是一种网络编程接口,实际上是作为TCP/IP协议的一种封装。可以通过调用WinSock的接口函数来调用TCP/IP的各种功能。

WinSock 编程简单流程:WinSock编程分为服务器端和客户端两部分。

2、实现的功能

1、显示上线下线时间  

2、服务端监听上线的客户端的IP和端口号

3、一个客户端发消息后(注:客户端在java编译器的终端输入消息)服务器能接收并转发消息给其他客户端     

4、清屏

如何在局域网中使用:  修改客户端代码中的  private final String HOST = "127.0.0.1" 改为接入的局域网服务器的IP即可

3、代码

  1. package GroupChat;
  2. import javax.swing.*;
  3. import javax.swing.table.DefaultTableModel;
  4. import javax.swing.table.TableModel;
  5. import java.awt.*;
  6. import java.awt.event.ActionEvent;
  7. import java.awt.event.ActionListener;
  8. import java.awt.event.MouseEvent;
  9. import java.awt.event.MouseListener;
  10. import java.io.OutputStream;
  11. import java.text.SimpleDateFormat;
  12. import java.util.ArrayList;
  13. import java.util.Date;
  14. class ClientFrame extends JFrame {
  15. //时间显示格式
  16. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  17. //窗口宽度
  18. final int WIDTH = 700;
  19. //窗口高度
  20. final int HEIGHT = 600;
  21. //创建发送按钮
  22. JButton btnSend = new JButton("发送");
  23. //创建清除按钮
  24. JButton btnClear = new JButton("清屏");
  25. //创建退出按钮
  26. JButton btnExit = new JButton("退出");
  27. //创建聊天消息框
  28. JTextArea jtaChat = new JTextArea();
  29. //当前在线列表的列标题
  30. String[] colTitles = {"IP", "端口"};
  31. //当前在线列表的数据,暂存在动态数组中
  32. String[][] rowDatatrue;
  33. public void setRowData(String address,String port){
  34. String[] newaddress = {address,port};
  35. jtaChat.append(sdf.format(new Date()) + "\n: " + address+":"+port + "上线了\n");
  36. DefaultTableModel model = (DefaultTableModel)jtbOnline.getModel();
  37. model.addRow(new Object[]{newaddress[0],newaddress[1]});
  38. jtbOnline.setModel(model);
  39. }
  40. public void addinfo(String msg){
  41. jtaChat.append(sdf.format(new Date()) + "\n: " + msg.trim() +"\n");
  42. }
  43. public void delRowData(String address,String port){
  44. String[] deladdress = {address,port};
  45. jtaChat.append(sdf.format(new Date()) + "\n: " + address+":"+port + "离线了\n");
  46. DefaultTableModel model = (DefaultTableModel)jtbOnline.getModel();
  47. int rowcount= model.getRowCount();
  48. for(int i=0;i<rowcount;i++){
  49. if(model.getValueAt(i,1).equals(deladdress[1])){
  50. model.removeRow(i);
  51. break;
  52. }
  53. }
  54. model.fireTableDataChanged();
  55. }
  56. //创建当前在线列表
  57. JTable jtbOnline = new JTable
  58. (
  59. new DefaultTableModel(rowDatatrue, colTitles) {
  60. //表格不可编辑,只可显示
  61. @Override
  62. public boolean isCellEditable(int row, int column) {
  63. return false;
  64. }
  65. }
  66. );
  67. //创建聊天消息框的滚动窗
  68. JScrollPane jspChat = new JScrollPane(jtaChat);
  69. //创建当前在线列表的滚动窗
  70. JScrollPane jspOnline = new JScrollPane(jtbOnline);
  71. //设置默认窗口属性,连接窗口组件
  72. public ClientFrame() {
  73. //标题
  74. setTitle("聊天室");
  75. //大小
  76. setSize(WIDTH, HEIGHT);
  77. //不可缩放
  78. setResizable(false);
  79. //设置布局:不适用默认布局,完全自定义
  80. setLayout(null);
  81. //设置按钮大小和位置
  82. btnClear.setBounds(140, 420, 100, 60);
  83. //设置按钮文本的字体
  84. btnClear.setFont(new Font("宋体", Font.BOLD, 18));
  85. //添加按钮
  86. this.add(btnClear);
  87. //聊天消息框自动换行
  88. jtaChat.setLineWrap(true);
  89. //聊天框不可编辑,只用来显示
  90. jtaChat.setEditable(false);
  91. //设置聊天框字体
  92. jtaChat.setFont(new Font("楷体", Font.BOLD, 16));
  93. //设置滚动窗的水平滚动条属性:不出现
  94. jspChat.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
  95. //设置滚动窗的垂直滚动条属性:需要时自动出现
  96. jspChat.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
  97. //设置滚动窗大小和位置
  98. jspChat.setBounds(20, 20, 360, 400);
  99. //添加聊天窗口的滚动窗
  100. this.add(jspChat);
  101. //设置滚动窗的水平滚动条属性:不出现
  102. jspOnline.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
  103. //设置滚动窗的垂直滚动条属性:需要时自动出现
  104. jspOnline.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
  105. //设置当前在线列表滚动窗大小和位置
  106. jspOnline.setBounds(420, 20, 250, 400);
  107. //添加当前在线列表
  108. this.add(jspOnline);
  109. //添加清屏按钮的响应事件
  110. btnClear.addActionListener
  111. (
  112. new ActionListener() {
  113. @Override
  114. public void actionPerformed(ActionEvent event) {
  115. //聊天框清屏
  116. jtaChat.setText("");
  117. }
  118. }
  119. );
  120. this.show();
  121. }
  122. }

客户端(想要几个客户就创建几个客户端) 

  1. package GroupChat;
  2. import java.io.IOException;
  3. import java.net.InetSocketAddress;
  4. import java.nio.ByteBuffer;
  5. import java.nio.channels.SelectionKey;
  6. import java.nio.channels.Selector;
  7. import java.nio.channels.SocketChannel;
  8. import java.util.Iterator;
  9. import java.util.Scanner;
  10. public class GroupChatClient {
  11. private final String HOST = "127.0.0.1";//服务器的ip
  12. private final int PORT = 6667;//服务器端口
  13. private Selector selector;
  14. private SocketChannel socketChannel;
  15. private String username;
  16. //构造器,完成初始化工作
  17. public GroupChatClient() throws IOException {
  18. selector = Selector.open();
  19. //连接服务器
  20. socketChannel = SocketChannel.open(new InetSocketAddress(HOST, PORT));
  21. //设置非阻塞
  22. socketChannel.configureBlocking(false);
  23. //将 channel 注册到selector
  24. socketChannel.register(selector, SelectionKey.OP_READ);
  25. //得到 username
  26. username = socketChannel.getLocalAddress().toString().substring(1);
  27. System.out.println(username + " is ok...");
  28. }
  29. //向服务器发送消息
  30. public void sendInfo(String info) {
  31. info = username + " 说:" + info;
  32. try {
  33. socketChannel.write(ByteBuffer.wrap(info.getBytes()));
  34. } catch (IOException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. //读取从服务器端回复的消息
  39. public void readInfo() {
  40. try {
  41. int readChannels = selector.select();
  42. if (readChannels > 0) {//有可以用的通道
  43. Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
  44. while (iterator.hasNext()) {
  45. SelectionKey key = iterator.next();
  46. if (key.isReadable()) {
  47. //得到相关的通道
  48. SocketChannel sc = (SocketChannel) key.channel();
  49. //得到一个 Buffer
  50. ByteBuffer buffer = ByteBuffer.allocate(1024);
  51. //读取
  52. sc.read(buffer);
  53. //把读到的缓冲区的数据转成字符串
  54. String msg = new String(buffer.array());
  55. System.out.println(msg.trim());
  56. }
  57. }
  58. iterator.remove(); //删除当前的 selectionKey,防止重复操作
  59. } else {
  60. //System.out.println("没有可以用的通道...");
  61. }
  62. } catch (Exception e) {
  63. e.printStackTrace();
  64. }
  65. }
  66. public static void main(String[] args) throws Exception {
  67. //启动我们客户端
  68. GroupChatClient chatClient = new GroupChatClient();
  69. //启动一个线程,每个 3 秒,读取从服务器发送数据
  70. new Thread() {
  71. public void run() {
  72. while (true) {
  73. chatClient.readInfo();
  74. try {
  75. Thread.currentThread().sleep(3000);
  76. } catch (InterruptedException e) {
  77. e.printStackTrace();
  78. }
  79. }
  80. }
  81. }.start();
  82. //发送数据给服务器端
  83. Scanner scanner = new Scanner(System.in);
  84. while (scanner.hasNextLine()) {
  85. String s = scanner.nextLine();
  86. chatClient.sendInfo(s);
  87. }
  88. }
  89. }

 服务器端

  1. package GroupChat;
  2. // 服务端:
  3. import java.io.IOException;
  4. import java.net.InetSocketAddress;
  5. import java.nio.ByteBuffer;
  6. import java.nio.channels.Channel;
  7. import java.nio.channels.SelectionKey;
  8. import java.nio.channels.Selector;
  9. import java.nio.channels.ServerSocketChannel;
  10. import java.nio.channels.SocketChannel;
  11. import java.util.Iterator;
  12. public class GroupChatServer {
  13. //定义属性
  14. private Selector selector;
  15. private ServerSocketChannel listenChannel;
  16. private ClientFrame clientFrame;
  17. private static final int PORT = 6667;
  18. //构造器
  19. //初始化工作
  20. public GroupChatServer() {
  21. clientFrame = new ClientFrame();
  22. try {
  23. //得到选择器
  24. selector = Selector.open();
  25. //ServerSocketChannel
  26. listenChannel = ServerSocketChannel.open();
  27. //绑定端口
  28. listenChannel.socket().bind(new InetSocketAddress(PORT));
  29. //设置非阻塞模式
  30. listenChannel.configureBlocking(false);
  31. //将该 listenChannel 注册到 selector
  32. listenChannel.register(selector, SelectionKey.OP_ACCEPT);
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. public void listen() {
  38. try {
  39. //循环处理
  40. while (true) {
  41. int count = selector.select();
  42. if (count > 0) { //有事件处理
  43. // 遍历得到 selectionKey 集合
  44. Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
  45. while (iterator.hasNext()) {
  46. //取出 selectionkey
  47. SelectionKey key = iterator.next();
  48. //监听到 accept
  49. if (key.isAcceptable()) {
  50. SocketChannel sc = listenChannel.accept();
  51. sc.configureBlocking(false);
  52. //将该 sc 注册到 seletor
  53. sc.register(selector, SelectionKey.OP_READ);
  54. //提示
  55. // System.out.println(sc.getRemoteAddress() + " 上线 ");
  56. String remoteaddress = sc.getRemoteAddress().toString();
  57. String[] split = remoteaddress.split(":");
  58. clientFrame.setRowData(split[0],split[1]);
  59. }
  60. if (key.isReadable()) {//通道发送read事件,即通道是可读的状态
  61. // 处理读(专门写方法..)
  62. readData(key);
  63. }
  64. //当前的 key 删除,防止重复处理
  65. iterator.remove();
  66. }
  67. } else {
  68. System.out.println("等待....");
  69. }
  70. }
  71. } catch (Exception e) {
  72. e.printStackTrace();
  73. } finally {
  74. //发生异常处理....
  75. }
  76. }
  77. //读取客户端消息
  78. public void readData(SelectionKey key) {
  79. SocketChannel channel = null;
  80. try {
  81. //得到 channel
  82. channel = (SocketChannel) key.channel();
  83. //创建 buffer
  84. ByteBuffer buffer = ByteBuffer.allocate(1024);
  85. int count = channel.read(buffer);
  86. //根据 count 的值做处理
  87. if (count > 0) {
  88. //把缓存区的数据转成字符串
  89. String msg = new String(buffer.array());
  90. //输出该消息
  91. System.out.println("form客户端:" + msg);
  92. clientFrame.addinfo(msg);
  93. //向其它的客户端转发消息(去掉自己),专门写一个方法来处理
  94. sendInfoToOtherClients(msg, channel);
  95. }
  96. } catch (IOException e) {
  97. try {
  98. System.out.println(channel.getRemoteAddress() + "离线了..");
  99. String remoteaddress = channel.getRemoteAddress().toString();
  100. String[] split = remoteaddress.split(":");
  101. clientFrame.delRowData(split[0],split[1]);
  102. //取消注册
  103. key.cancel();
  104. //关闭通道
  105. channel.close();
  106. } catch (IOException e2) {
  107. e2.printStackTrace();
  108. }
  109. }
  110. }
  111. //转发消息给其它客户(通道)
  112. private void sendInfoToOtherClients(String msg, SocketChannel self) throws IOException {
  113. System.out.println("服务器转发消息中...");
  114. //遍历所有注册到 selector 上的 SocketChannel,并排除 self
  115. for (SelectionKey key : selector.keys()) {
  116. //通过 key 取出对应的 SocketChannel
  117. Channel targetChannel = key.channel();
  118. //排除自己
  119. if (targetChannel instanceof SocketChannel && targetChannel != self) {
  120. //转型
  121. SocketChannel dest = (SocketChannel) targetChannel;
  122. //将 msg 存储到 buffer
  123. ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
  124. //将 buffer 的数据写入通道
  125. dest.write(buffer);
  126. }
  127. }
  128. }
  129. public static void main(String[] args) {
  130. //创建服务器对象
  131. GroupChatServer groupChatServer = new GroupChatServer();
  132. groupChatServer.listen();
  133. }
  134. }

运行结果 

客户端命令行:  

                         

 客户端1:

  

客户端2:                                 

客户端3:

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/728218
推荐阅读
相关标签
  

闽ICP备14008679号