1.Socket基础知识

  Socket(套接字)用于描述IP地址和端口,是通信链的句柄,应用程序可以通过Socket向网络发出请求或者应答网络请求。

  Socket是支持TCP/IP协议的网络通信的基本操作单元,是对网络通信过程中端点的抽象表示,包含了进行网络通信所必需的5种信息:连接所使用的协议、本地主机的IP地址、本地进程的协议端口、远地主机的IP地址以及远地进程的协议端口。

1.1 Socket的传输模式

  Socket有两种主要的操作方式:面向连接的和无连接的。(TCP/UDP)

  面向连接的Socket操作就像一部电话,Socket必须在发送数据之前与目的地的Socket取得连接,一旦连接建立了,Socket就可以使用一个流接口进行打开、读写以及关闭操作。并且,所有发送的数据在另一端都会以相同的顺序被接收。

  无连接的Socket操作就像一个邮件投递,每一个数据报都是一个独立的单元,它包含了这次投递的所有信息(目的地址和要发送的内容)。在这个模式下的Socket不需要连接目的地Socket,它只是简单的投出数据报。

  由此可见,无连接的操作是快速高效的,但是数据安全性不佳;面向连接的操作效率较低,但数据的安全性较好。

  本文主要介绍的是面向连接的Socket操作。

1.2 Socket的构造方法

  Java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的Socket客户端和服务器端。

  Socket的构造方法如下:

  (1)Socket(InetAddress address, int port);

  (2)Socket(InetAddress address, int port, boolean stream);

  (3)Socket(String host, int port);

  (4)Socket(String host, int port, boolean stream);

  (5)Socket(SocketImpl impl);

  (6)Socket(String host, int port, InetAddress localAddr, int localPort);

  (7)Socket(InetAddress address, int port, InetAddrss localAddr, int localPort);

  ServerSocket的构造方法如下:

  (1)ServerSocket(int port);

  (2)ServerSocket(int port, int backlog);

  (3)ServerSocket(int port, int backlog, InetAddress bindAddr);

  其中,参数address、host和port分别是双向连接中另一方的IP地址、主机名和端口号;参数stream表示Socket是流Socket还是数据报Socket;参数localAddr和localPort表示本地主机的IP地址和端口号;SocketImpl是Socket的父类,既可以用来创建ServerSocket,也可以用来创建Socket。

  如下的代码在服务器端创建了一个ServerSocket:

1   try {
2   ServerSocket serverSocket = new ServerSocket(50000);    //创建一个ServerSocket,用于监听客户端Socket的连接请求
3       while(true) {
4           Socket socket = serverSocket.accept();        //每当接收到客户端的Socket请求,服务器端也相应的创建一个Socket
5       //todo开始进行Socket通信
6       }
7   }catch (IOException e) {
8       e.printStackTrace();
9   }

  其中,50000是我们自己选择的用来进行Socket通信的端口号,在创建Socket时,如果该端口号已经被别的服务占用,将会抛出异常。

  通过以上的代码,我们创建了一个ServerSocket在端口50000监听客户端的请求。accept()是一个阻塞函数,就是说该方法被调用后就会一直等待客户端的请求,直到有一个客户端启动并请求连接到相同的端口,然后accept()返回一个对应于该客户端的Socket。

  那么,如何在客户端创建并启动一个Socket呢?

1   try {
2       socket = new Socket("192.168.1.101", 50000);    //192.168.1.101是服务器的IP地址,50000是端口号
3     //todo开始进行Socket通信
4   } catch (IOException e) {
5       e.printStackTrace();
6 }

  至此,客户端和服务器端都建立了用于通信的Socket,接下来就可以由各自的Socket分别打开各自的输入流和输出流进行通信了。

1.3输入流和输出流

  Socket提供了方法getInputStream()和getOutPutStream()来获得对应的输入流和输出流,以便对Socket进行读写操作,这两个方法的返回值分别是InputStream和OutPutStream对象。

  为了便于读写数据,我们可以在返回的输入输出流对象上建立过滤流,如PrintStream、InputStreamReader和OutputStreamWriter等。

1.4关闭Socket

  可以通过调用Socket的close()方法来关闭Socket。在关闭Socket之前,应该先关闭与Socket有关的所有输入输出流,然后再关闭Socket。

2.简易聊天室

  下面就来说说如何通过Socket编程实现一个简易聊天室。客户端完成后的运行效果如图1所示。

  图1 运行效果

  在该客户端的界面中,使用了一个TextView控件来显示聊天记录。为了方便查看,将两个用户也放到了一个界面中,实际上应该启动两个模拟器,分别作为两个用户的客户端,此处是为了方便操作才这么做的。

2.1服务器端ServerSocket的实现

  在该实例中,我们在MyEclipse中新建了一个Java工程作为服务器端。在该Java工程中,我们应该完成以下的操作。

  (1)指定端口实例化一个ServerSocket,并调用ServerSocket的accept()方法在等待客户端连接期间造成阻塞。

  (2)每当接收到客户端的Socket请求时,服务器端也相应的创建一个Socket,并将该Socket存入ArrayList中。与此同时,启动一个ServerThread线程来为该客户端Socket服务。

  以上两步操作,可以通过以下的代码来实现:

 1   /*
 2    * Class    :   MyServer类,用于监听客户端Socket连接请求
 3    * Author   :   博客园-依旧淡然
 4    */
 5   public class MyServer {
 6   
 7       //定义ServerSocket的端口号
 8       private static final int SOCKET_PORT = 50000;
 9       //使用ArrayList存储所有的Socket
10       public static ArrayList<Socket> socketList = new ArrayList<Socket>();
11   
12       public void initMyServer() {
13           try {
14         //创建一个ServerSocket,用于监听客户端Socket的连接请求
15               ServerSocket serverSocket = new ServerSocket(SOCKET_PORT);
16               while(true) {
17                   //每当接收到客户端的Socket请求,服务器端也相应的创建一个Socket
18                   Socket socket = serverSocket.accept();
19                   socketList.add(socket);
20                   //每连接一个客户端,启动一个ServerThread线程为该客户端服务
21                   new Thread(new ServerThread(socket)).start();
22               }
23           }catch (IOException e) {
24               e.printStackTrace();
25           }
26       }
27   
28       public static void main(String[] args) {
29           MyServer myServer = new MyServer();
30           myServer.initMyServer();
31       }
32   }

  (3)在启动的ServerThread线程中,我们需要将读到的客户端内容(也就是某一个客户端Socket发送给服务器端的数据),发送给其他的所有客户端Socket,实现信息的广播。ServerThread类的具体实现如下:

 1   public class ServerThread implements Runnable {
 2   
 3       //定义当前线程所处理的Socket
 4       private Socket socket = null;
 5       //该线程所处理的Socket对应的输入流
 6       private BufferedReader bufferedReader = null;
 7   
 8       /*
 9        * Function  :    ServerThread的构造方法
10        * Author    :    博客园-依旧淡然
11        */
12       public ServerThread(Socket socket) throws IOException {
13           this.socket = socket;
14           //获取该socket对应的输入流
15           bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
16       }
17   
18       /*
19        * Function  :    实现run()方法,将读到的客户端内容进行广播
20        * Author    :    博客园-依旧淡然
21        */
22       public void run() {
23           try {
24               String content = null;
25               //采用循环不断地从Socket中读取客户端发送过来的数据
26               while((content = bufferedReader.readLine()) != null) {
27                   //将读到的内容向每个Socket发送一次
28                   for(Socket socket : MyServer.socketList) {
29                       //获取该socket对应的输出流
30                       PrintStream printStream = new PrintStream(socket.getOutputStream());
31                       //向该输出流中写入要广播的内容
32                       printStream.println(packMessage(content));
33   
34                   }
35               }
36           } catch(IOException e) {
37               e.printStackTrace();
38           }
39       }
40   
41       /*
42        * Function  :    对要广播的数据进行包装
43        * Author    :    博客园-依旧淡然
44        */
45       private String packMessage(String content) {
46           String result = null;
47           SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");    //设置日期格式
48           if(content.startsWith("USER_ONE")) {
49               String message = content.substring(8);        //获取用户发送的真实的信息
50               result = "\n" + "往事如风  " + df.format(new Date()) + "\n" + message;
51           }
52           if(content.startsWith("USER_TWO")) {
53               String message = content.substring(8);        //获取用户发送的真实的信息
54               result = "\n" + "依旧淡然  " + df.format(new Date()) + "\n" + message;
55           }
56           return result;
57       }
58   
59   }

  其中,在packMessage()方法中,我们对要广播的数据进行了包装。因为要分辨出服务器接收到的消息是来自哪一个客户端Socket的,我们对客户端Socket发送的消息也进行了包装,方法是在消息的头部加上"USER_ONE"来代表用户"往事如风",在消息的头部加上"USER_TWO"来代表用户"依旧淡然"。

  至此,服务器端的ServerSocket便算是创建好了。

2.2客户端Socket的实现

  接下来,我们便可以在Android工程中,分别为用户"往事如风"和"依旧淡然"创建一个客户端Socket,并启动一个客户端线程ClientThread来监听服务器发来的数据。

  这一过程的具体实现如下:

 1     /*
 2      * Function   :   初始化Socket
 3      * Author     :   博客园-依旧淡然
 4      */
 5     private void initSocket() {
 6         try {
 7             socketUser1 = new Socket(URL_PATH, SOCKET_PORT);            //用户1的客户端Socket
 8             socketUser2 = new Socket(URL_PATH, SOCKET_PORT);            //用户2的客户端Socket
 9             clientThread = new ClientThread();        //客户端启动ClientThread线程,读取来自服务器的数据
10             clientThread.start();
11         } catch (IOException e) {
12             e.printStackTrace();
13         }
14     }

  ClientThread的具体实现和服务器端的ServerThread线程相似,唯一的区别是,在ClientThread线程中接收到服务器端发来的数据后,我们不可以直接在ClientThread线程中进行刷新UI的操作,而是应该将数据封装到Message中,再调用MyHandler对象的sendMessage()方法将Message发送出去。这一过程的具体实现如下:

 1     /*
 2      * Function   :   run()方法,用于读取来自服务器的数据
 3      * Author     :   博客园-依旧淡然
 4      */
 5   public void run() {
 6   try {
 7           String content = null;
 8           while((content = bufferedReader .readLine()) != null) {
 9               Bundle bundle = new Bundle();
10               bundle.putString(KEY_CONTENT, content);
11               Message msg = new Message();
12               msg.setData(bundle);            //将数据封装到Message对象中
13               myHandler.sendMessage(msg);
14           }
15       } catch (Exception e) {
16           e.printStackTrace();
17       }
18   }

  最后,我们在UI主线程中创建一个内部类MyHandler,让它继承Handler类,并实现handleMessage()方法,用来接收Message消息并处理(刷新UI)。MyContent是一个用来保存聊天记录的类,提供了get和set接口,其中,set接口设置的本条聊天记录,而get接口获得的是全部的聊天记录。具体的实现如下:

 1     /*
 2      * Class      :   内部类MyHandler,用于接收消息并处理
 3      * Author     :   博客园-依旧淡然
 4      */
 5     private class MyHandler extends Handler {
 6         public void handleMessage(Message msg) {
 7             Bundle bundle = msg.getData();            //获取Message中发送过来的数据
 8             String content = bundle.getString(KEY_CONTENT);
 9             MyContent.setContent(content);            //保存聊天记录
10             mTextView.setText(MyContent.getContent());
11         }
12     }

  至此,客户端的Socket也编写完成了。

转载于:https://www.cnblogs.com/berylqliu/p/6261505.html

Socket编程实现简易聊天室相关推荐

  1. Express+Socket.IO 实现简易聊天室

    代码地址如下: http://www.demodashi.com/demo/12477.html 闲暇之余研究了一下 Socket.io,搭建了一个简易版的聊天室,如有不对之处还望指正,先上效果图: ...

  2. node+socket.io+HTTP简易聊天室

    用node+socket.io+HTTP做一个聊天室.聊天室主要由两部分构成,服务器(index.js)以及客户端(index.html). 文件目录如下所示: (node_modules文件夹以及p ...

  3. 【Socket网络编程进阶与实战】-----简易聊天室案例

    前言 本篇博客实现:简易聊天室 聊天室案例: 聊天室数据传输设计 必要条件:客户端.服务器 必要约束:数据传输协议 原理: 服务器监听消息来源,客户端链接服务器并发送消息到服务器.

  4. Java WebSocket编程与网页简易聊天室

    在webSocket还未引入前,许多开发人员通过各种非正规手段来完成更新网站的最新信息和到所有当前访问者的任务,其中一种手段就是通过浏览器向服务器轮询更新,但这种手段的网络延迟比较明显,其用户体验比较 ...

  5. java聊天室程序_Java简易聊天室程序socket

    Java简易聊天室程序socket chatroomdemo.java package com.socket.demo; import java.io.IOException; import java ...

  6. express+socket简易聊天室

    文章目录 简易聊天室 前置知识 这里使用socket.io实现 前端设置 前端代码 后端代码 效果图 娱乐一刻 简易聊天室 前置知识 在我们平常的时候,ajax发送的都是短连接,get完成或者post ...

  7. 2020暑假集训项目——Java简易聊天室

    经过一周的学习与搬砖,我成功的完成了暑假集训的第一个项目--Java简易聊天室,这里对整个项目做一个总结.(文末附下载地址) 本项目支持的功能: 1.可同时开启多个客户端进行多人聊天: 2.可与在线的 ...

  8. HTML5 之WebSocket入门demo和简易聊天室

    HTML5 WebSocket WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议. 在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏 ...

  9. 网络编程之java简易聊天室实现

    最近浅学习了一些关于网络编程方面的知识,视频是跟着狂神学习的,可能学习的不是很深 说到网络,相信大家都对TCP.UDP和HTTP协议这些都不是很陌生,学习这部分应该先对端口.Ip地址这些基础知识有一定 ...

最新文章

  1. 需求:过滤下面这个网页里共723行 校对中里 行数为两位数的 行 并设置sz和rz在Windows和Linux之间发送和接收文件不用搭FTP...
  2. spring依赖注入原理(转载)
  3. cad2016中选择全图字体怎么操作_打开CAD图纸字体丢失、重新选择怎么办?这样设置,一辈子用的到...
  4. Convert、Parse、TryParse、(int)等区别(细节小记)
  5. 单反相机参数的直观认识
  6. Webservice 用http get方式无法请求到的解决办法
  7. [WebApi] 捣鼓一个资源管理器--服务器端分割压缩图片
  8. python实现键盘打字练习
  9. C语言实现移位密码算法,仿射密码算法
  10. 入行 AI,如何选个脚踏实地的岗位?
  11. 【Redis核心原理和应用实践】应用 1:千帆竞发 —— 分布式锁
  12. 如何在Word中用Visio画图
  13. 记录安装Ubuntu16.04后必须要做的事,杂篇
  14. R语言绘制PCoA图
  15. BGP综合认知及配置
  16. 利用redis缓存对 list集合中的数据 进行分页操作(一)
  17. 全球最大 IPO,我们能否赚笔养老钱?
  18. java 时分秒 转换 秒_JAVA将时分秒格式的时间转化成秒数
  19. android监控电话录音,Android例子源码实现电话录音监听的安卓例子
  20. CASS11解决细等线字体样式显示为问号

热门文章

  1. Spring期末试题
  2. Requests 2.18.1文档
  3. StringBuilder与StringBuffer比较
  4. 《深度学习入门》实现三层神经网络前向传播
  5. 搭建hadoop2.6.0集群环境
  6. SharedPreferences基础
  7. 基于SSD的Kafka应用层缓存架构设计与实现
  8. 一次CMS GC问题排查过程(理解原理+读懂GC日志)
  9. 设置表字段大小写敏感
  10. MySQL Gap Lock问题