Duan

The more hard, the more fortunate.


  • 首页

  • 关于

  • 归档

  • 标签

使用 Socket 和 ServerSocket 实现简单的聊天程序

用java.net.Socket和java.net.ServerSocket实现简单的聊天程序

思路是这样的:
假设用户A与用户B进行聊天,用户B端使用ServerSocket作为服务端,而用户A使用Socket与用户B进行通信。

  • 这两个小程序需要运行在同一台电脑上,才能正常通信。
  • 运行时一定要先运行Server.class再运行Client.class。
  • 通过异常来控制程序逻辑不是首选方式,但想了很久也没想出其它方法 >.<
用户A的代码如下(Client.java)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by DuanJiaNing on 2017/3/16.
*/
public class Client {
public static void main(String[] args) {
try {
//创建Socket连接到本机的2003端口
Socket socket = new Socket(InetAddress.getLocalHost(), 2003);
println("**********您正在和:" + InetAddress.getLocalHost().getHostAddress() + " 聊天***********\n" + "输入信息,按回车即可发送!输入quit可结束会话并关闭程序。");
OutputStream out = socket.getOutputStream();
out.write("Hello 在么?".getBytes());
//创建新线程负责打印接收到的消息
new Thread(new ClientThread(socket.getInputStream())).start();
//接收控制台输入
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//读取控制台输入,有输入且不为quit时进行发送
String line;
while ((line = bufr.readLine()) != null && !line.equals("quit")) {
out.write(line.getBytes());
}
//关闭套接字
//程序结束 这将导致ClientThread抛java.net.SocketException: Socket closed异常而结束(暂且就这样关闭这个线程)
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
static class ClientThread implements Runnable {
InputStream input;
public ClientThread(InputStream in) {
this.input = in;
}
public void run() {
byte[] bytes = new byte[1024];
int len;
try {
//当收到消息时把消息打印到控制台
while ((len = input.read(bytes)) != -1) {
println(new String(bytes, 0, len));
}
} catch (Exception e) {
//当服务端或客户端结束会话时(输入quit时)会关闭socket从而抛出java.net.SocketException: Socket closed异常
//隐藏此异常不做处理让程序继续“正常”运行(正常结束)
if (!(e instanceof SocketException))
e.printStackTrace();
}
println("**********此次会话结束**********");
System.exit(0);
}
}
private static void println(String msg) {
Date date = new Date();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ");
System.out.println(format.format(date) + msg);
}
}

用户B代码(服务端)(Server.java)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by DuanJiaNing on 2017/3/16.
*/
public class Server {
private static BufferedReader reader;
public static void main(String[] args) {
//监听本机(服务器)的2003端口
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(2003);
} catch (IOException e) {
e.printStackTrace();
}
while (true) {
try {
println("************当前没有正在进行的会话***********");
//accept为阻塞时方法,当有连接时会返回连接着的socket对象
Socket socket = serverSocket.accept();
println("当前聊天对象来自:" + socket.getLocalAddress().getHostAddress() + "\n" + "输入信息,按回车即可发送!输入quit可结束此次会话。");
//创建新线程负责打印接收到的消息
InputStream in = socket.getInputStream();
new Thread(new ServerThread(in)).start();
//接收输入并发送给连接者
reader = new BufferedReader(new InputStreamReader(System.in));
String line;
OutputStream out = socket.getOutputStream();
while ((line = reader.readLine()) != null && !line.equals("quit")) {
out.write(line.getBytes());
}
//服务端主动结束会话
out.write("先不聊了。bye~".getBytes());
//关闭套接字
//程序结束 这将导致ServerThread抛java.net.SocketException: Socket closed异常而结束(暂且就这样关闭此次会话对应的线程)
socket.close();
} catch (Exception e) {
//当客户端主动结束会话(输入quit时),而服务端试图发送信息时会抛出java.net.SocketException: Software caused connection abort: socket write error
//借这个异常让服务端回到等待客户端接入状态
if (e instanceof SocketException) {
println("您的聊天对象已离线\n");
continue;
}
else
e.printStackTrace();
}
}
}
static class ServerThread implements Runnable {
InputStream input;
public ServerThread(InputStream in) {
this.input = in;
}
public void run() {
byte[] bytes = new byte[1024];
int len;
try {
//当收到消息时把消息打印到控制台
while ((len = input.read(bytes)) != -1) {
println(new String(bytes, 0, len));
}
} catch (Exception e) {
//当服务端或(输入quit时)会关闭socket从而抛出java.net.SocketException: Socket closed异常
//当服务端输入quit时隐藏此异常不做处理让程序继续运行(回到等待客户端接入的状态)
if (!(e instanceof SocketException)) {
e.printStackTrace();
}
}
}
}
private static void println(String msg) {
Date date = new Date();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ");
System.out.println(format.format(date) + msg);
}
}

编译运行
  • 分别编译Server.java,Client.java文件

    1
    2
    javac Server.java
    javac Client.java
  • 先后运行Server.class和Client.class

    1
    2
    java Server
    java Client
  • 此时可看到:
    这里写图片描述

  • 接下来进行简单对话后在服务端输入quit可看到:
    这里写图片描述

  • 此时客户端已经关闭,而服务端会循环阻塞在accept方法上。此时只需再次运行Client.class(在dos终端切换到Client.class文件所在路径后输入java Client)就能再次连接到服务端

  • 服务端主动结束会话,此时时这样的:
    这里写图片描述
有哪里可以改进的可以留言告诉我哦

END
James Duan

James Duan

¥.¥

23 日志
43 标签
Github Twitter QQ CSDN email
© Thu May 18 2017 08:00:00 GMT+0800 (中国标准时间) - 2017 James Duan
由 Hexo 强力驱动
主题 - NexT.Mist