Socket编程实现简易聊天室

1.Socket基础知识

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

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

1.1Socket的传输模式

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

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

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

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

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

1.2Socket的构造方法

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

Socket的构造方法如下:

(1)Socket(InetAddressaddress,intport);

(2)Socket(InetAddressaddress,intport,booleanstream);

(3)Socket(Stringhost,intport);

(4)Socket(Stringhost,intport,booleanstream);

(5)Socket(SocketImplimpl);

(6)Socket(Stringhost,intport,InetAddresslocalAddr,intlocalPort);

(7)Socket(InetAddressaddress,intport,InetAddrsslocalAddr,intlocalPort);

ServerSocket的构造方法如下:

(1)ServerSocket(intport);

(2)ServerSocket(intport,intbacklog);

(3)ServerSocket(intport,intbacklog,InetAddressbindAddr);

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

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

[这里是图片001]

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   }

[这里是图片002]

其中,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服务。

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

[这里是图片004]

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 socketList = new ArrayList();
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   }

[这里是图片005]

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

[这里是图片006]

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 = "
" + "往事如风 " + df.format(new Date()) + "
" + message;
51    }
52    if(content.startsWith(“USER_TWO”)) {
53    String message = content.substring(8); //获取用户发送的真实的信息
54    result = "
" + "依旧淡然 " + df.format(new Date()) + "
" + message;
55    }
56    return result;
57    }
58   
59   }

[这里是图片007]

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

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

2.2客户端Socket的实现

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

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

[这里是图片008]

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 }

[这里是图片009]

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

[这里是图片010]

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   }

[这里是图片011]

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

[这里是图片012]

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 }

[这里是图片013]

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

  • 6
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Socket是一种网络编程的通信协议,它能够实现计算机之间的通信。要实现一个聊天室,可以使用Socket来建立一个服务器和多个客户端之间的连接。 首先,我们需要创建一个服务器端的Socket,让它监听一个特定的端口。这样当客户端尝试连接到这个端口时,服务器就能够接收到连接请求。一旦连接成功,服务器和客户端之间就可以进行通信了。 服务器端可以使用多线程来处理多个客户端的连接请求。每当有新的客户端连接到服务器时,就创建一个新的线程来处理与这个客户端的通信。服务器可以接收来自客户端的消息,并将这些消息广播给所有其他客户端,从而实现群聊功能。 而客户端需要创建一个Socket来连接到服务器。客户端可以输入消息并通过Socket发送给服务器,然后等待服务器的广播消息。客户端也可以接收服务器传递过来的其他客户端发送的消息,从而实现与其他人的聊天功能。 在聊天室中,还可以添加一些额外的功能,比如私聊、发送文件等。私聊功能可以通过在消息中添加目标用户的标识来实现,使得只有目标用户能够接收到该条消息。发送文件功能可以通过将文件内容进行分割,并通过Socket逐个发送分割后的数据包。 总之,通过使用Socket协议,我们可以很方便地实现一个聊天室服务器端和多个客户端之间的通信通过Socket建立连接,实现了消息的传递和广播,从而实现聊天功能。同时,我们还可以扩展聊天室的功能,使得用户能够进行私聊、发送文件等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值