Markdown版本笔记 | 我的GitHub首页 | 我的博客 | 我的微信 | 我的邮箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
TCP Socket 即时通讯 API 示例
public class SocketActivity extends ListActivity implements Client.MsgListener {
public static final int PORT = 11232;
public static final String HOST = "192.168.1.187"; //此 IP 为内网 IP ,所有只有在同一局域网下才能通讯(连接同一WIFI即可)
private TextView msgTextView;
private EditText editText;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] array = {"开启服务器",
"开启客户端",
"客户端发消息",
"客户端下线"};
setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, Arrays.asList(array)));
editText = new EditText(this);
getListView().addFooterView(editText);
msgTextView = new TextView(this);
getListView().addFooterView(msgTextView);
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
switch (position) {
case 0:
Server.SINGLETON.startServer(PORT);
break;
case 1:
Client.SINGLETON.startClient(HOST, PORT);
Client.SINGLETON.setListener(this);
break;
case 2:
Client.SINGLETON.sendMsg(editText.getText().toString());
break;
case 3:
Client.SINGLETON.exit();
break;
default:
break;
}
}
private SpannableString getSpannableString(String string, int color) {
SpannableString mSpannableString = new SpannableString(string);
ForegroundColorSpan colorSpan = new ForegroundColorSpan(color);
mSpannableString.setSpan(colorSpan, 0, mSpannableString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return mSpannableString;
}
@Override
public void onReveiveMsg(String message) {
runOnUiThread(() -> msgTextView.append(getSpannableString(message + "\n", Color.BLUE)));
}
@Override
public void onSendMsg(String message) {
runOnUiThread(() -> {
String text = Client.SINGLETON.getName() + " : " + editText.getText().toString() + "\n";
msgTextView.append(getSpannableString(text, Color.RED));
});
}
}
public enum Server {
SINGLETON;
public static final int MAX_TEXT_SIZE = 1024;
public static final String CLIENT_EXIT_CMD = "拜拜";
public static final String CHARSET = "GBK";
private Set
private boolean serverExit = false;
private ServerSocket server;//服务器对象
Server() {
socketSet = new HashSet<>();//用户集合
}
public void startServer(int port) {
if (server != null && !server.isClosed()) {
System.out.println("服务器已开启,不需要重复开启");
} else {
new Thread(() -> {
try {
server = new ServerSocket(port);
} catch (IOException e) {
e.printStackTrace();
System.out.println("服务器启动失败");
return;
}
System.out.println("服务器启动成功");
//循环监听
while (!serverExit) {
try {
Socket socket = server.accept();//获取连接过来的客户端对象。阻塞方法
socketSet.add(socket);
sendUserMsg(socket, getName(socket) + " 上线了, 当前 " + socketSet.size() + " 人在线");
listenerUserMsg(socket); //监听客户端发送的消息,并转发给其他用户
} catch (IOException e) {//用户下线
e.printStackTrace();
}
}
System.out.println("服务器已关闭");
}).start();
}
}
private void listenerUserMsg(Socket socket) {
new Thread(() -> {
try {
byte[] bytes = new byte[MAX_TEXT_SIZE];
int count;
boolean clinetExit = false;
while (!serverExit && !clinetExit && !socket.isClosed() && (count = socket.getInputStream().read(bytes)) != -1) {
String text = new String(bytes, 0, count, CHARSET);
System.out.println("服务器已收到【" + getName(socket) + "】发送的信息【" + text + "】");
clinetExit = CLIENT_EXIT_CMD.equals(text);
sendUserMsg(socket, getName(socket) + " : " + text);
}
} catch (IOException e) {//关闭与此用户相关的流
e.printStackTrace();
System.out.println(getName(socket) + " 异常掉线");
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socketSet.remove(socket);
sendUserMsg(socket, getName(socket) + " 下线了, 当前 " + socketSet.size() + " 人在线");
}
}
}).start();
}
private void sendUserMsg(Socket socket, String text) {
for (Socket otherSocket : socketSet) {//遍历所有的在线用户
if (otherSocket != null && !otherSocket.isClosed() && !otherSocket.equals(socket)) {
try {
OutputStream outputStream = otherSocket.getOutputStream();//获取相应的输出流,把信息发送过去
outputStream.write(text.getBytes(CHARSET));
outputStream.flush();
System.out.println("服务器已转发信息【" + text + "】给【" + getName(otherSocket) + "】");
} catch (IOException e) {
e.printStackTrace();
System.out.println(getName(socket) + " 异常");
}
}
}
}
private String getName(Socket socket) {
return "用户" + socket.getInetAddress().getHostAddress() + "_" + socket.getPort();
}
}
public enum Client {
SINGLETON;
Client() {
}
public static final int MAX_TEXT_SIZE = 1024;
public static final String CLIENT_EXIT_CMD = "拜拜";
public static final String CHARSET = "GBK";
private boolean exit = false;
private Socket socket;
private MsgListener listener;
public void startClient(String host, int port) {
if (socket != null && !socket.isClosed()) {
System.out.println("客户端已开启,不需要重复开启");
} else {
new Thread(() -> {
try {
socket = new Socket(host, port);//创建客户端对象
listenerUserMsg(); //监听消息
System.out.println("客户端已开启成功");
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端已开启失败");
}
}).start();
}
}
private void listenerUserMsg() {
new Thread(() -> {
try {
byte[] bytes = new byte[MAX_TEXT_SIZE];
int count;
while (!exit && socket != null && !socket.isClosed() && (count = socket.getInputStream().read(bytes)) != -1) {
String text = new String(bytes, 0, count, CHARSET);
System.out.println(getName() + " 收到信息【" + text + "】");
if (listener != null) {
listener.onReveiveMsg(text);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
public void sendMsg(String text) {
new Thread(() -> {
if (socket != null && !socket.isClosed()) {
try {
socket.getOutputStream().write(text.getBytes(CHARSET));//获取socket流中的输出流将指定的数据写出去
if (listener != null) {
listener.onSendMsg(text);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
public void exit() {
new Thread(() -> {
exit = true;
if (socket != null && !socket.isClosed()) {
try {
socket.getOutputStream().write(CLIENT_EXIT_CMD.getBytes(CHARSET));
socket.close();
System.out.println("客户端下线成功");
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端下线异常");
}
} else {
System.out.println("客户端已下线,不需要重复下线");
}
}).start();
}
public String getName() {
return "用户" + socket.getInetAddress().getHostAddress() + "_" + socket.getPort();
}
public void setListener(MsgListener listener) {
this.listener = listener;
}
public interface MsgListener {
void onReveiveMsg(String message);
void onSendMsg(String message);
}
}
BindException:Address already in use: JVM_Bind
该异常发生在服务器端进行new ServerSocket(port)
操作时。异常的原因是此port端口已经被占用。此时用netstat –an
命令,可以看到一个Listending状态的端口。只需要找一个没有被占用的端口就能解决这个问题。 ConnectException: Connection refused: connect
该异常发生在客户端进行new Socket(ip, port)
操作时,该异常发生的原因是或者具有此ip地址的机器不能找到,或者是该ip存在但找不到指定的端口进行监听。出现该问题,首先检查客户端的ip和port是否写错了,如果正确则从客户端ping
一下服务器看是否能ping通,如果能ping通再看在服务器端的监听指定端口的程序是否启动。 Socket is closed
该异常在客户端和服务器均可能发生。异常的原因是连接已被关闭后(调用了Socket的close方法)再对网络连接进行读写操作。 SocketException:(Connection reset 或者 Connect reset by peer:Socket write error)
该异常在客户端和服务器端均有可能发生,引起该异常的原因有两个,第一个就是如果一端的Socket被关闭,另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer)。另一个是一端退出,但退出时并未关闭该连接,另一端如果在从连接中读数据则抛出该异常(Connection reset)。简单的说就是在连接断开后的读和写操作引起的。 SocketException: Broken pipe
该异常在客户端和服务器均有可能发生。在上面那种异常的第一种情况中(也就是抛出SocketExcepton:Connect reset by peer:Socket write error),如果再继续写数据则抛出该异常。前两个异常的解决方法是首先确保程序退出前关闭所有的网络连接,其次是要检测对方的关闭连接操作,发现对方关闭连接后自己也要关闭该连接。构造方法
ServerSocket(int port)
创建绑定到特定端口的服务器套接字。常用方法
Socket accept()
侦听并接受到此套接字的连接。void close()
关闭此套接字。int getLocalPort()
返回此套接字在其上侦听的端口。boolean isClosed()
返回 ServerSocket 的关闭状态。构造方法
Socket(InetAddress address, int port)
创建一个流套接字并将其连接到指定IP地址的指定端口号Socket(String host, int port)
创建一个流套接字并将其连接到指定主机上的指定端口号。常用方法
void close()
关闭此套接字。InetAddress getInetAddress()
返回套接字连接的地址。InputStream getInputStream()
返回此套接字的输入流。int getLocalPort()
返回此套接字绑定到的本地端口。OutputStream getOutputStream()
返回此套接字的输出流。int getPort()
返回此套接字连接到的远程端口。boolean isClosed()
返回套接字的关闭状态。boolean isConnected()
返回套接字的连接状态。2018-11-23