热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

TCPSocket即时通讯API示例

Markdown版本笔记我的GitHub首页我的博客我的微信我的邮箱MyAndroidBlogsbaiqiantaobaiqiantaobqt20094baiqiantao@sin


















Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

TCP Socket 即时通讯 API 示例



目录
目录
案例
SocketActivity
服务端 Server
客户端 Client
常见异常
API
ServerSocket
Socket

案例

SocketActivity


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));
});
}
}


服务端 Server


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 socketSet;
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();
}
}


客户端 Client


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),如果再继续写数据则抛出该异常。前两个异常的解决方法是首先确保程序退出前关闭所有的网络连接,其次是要检测对方的关闭连接操作,发现对方关闭连接后自己也要关闭该连接。


API

ServerSocket

构造方法


  • ServerSocket() 创建非绑定服务器套接字。

  • ServerSocket(int port) 创建绑定到特定端口的服务器套接字。

  • ServerSocket(int port, int backlog) 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。

  • ServerSocket(int port, int backlog, InetAddress bindAddr) 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。

常用方法


  • Socket accept() 侦听并接受到此套接字的连接。

  • void bind(SocketAddress endpoint) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。

  • void bind(SocketAddress endpoint, int backlog) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。

  • void close() 关闭此套接字。

  • ServerSocketChannel getChannel() 返回与此套接字关联的唯一 ServerSocketChannel 对象(如果有)。

  • InetAddress getInetAddress() 返回此服务器套接字的本地地址。

  • int getLocalPort() 返回此套接字在其上侦听的端口。

  • SocketAddress getLocalSocketAddress() 返回此套接字绑定的端点的地址,如果尚未绑定则返回 null。

  • int getReceiveBufferSize() 获取此 ServerSocket 的 SO_RCVBUF 选项的值,该值是将用于从此 ServerSocket 接受的套接字的建议缓冲区大小。

  • boolean getReuseAddress() 测试是否启用 SO_REUSEADDR。

  • int getSoTimeout() 获取 SO_TIMEOUT 的设置。

  • protected void implAccept(Socket s) ServerSocket 的子类使用此方法重写 accept() 以返回它们自己的套接字子类。

  • boolean isBound() 返回 ServerSocket 的绑定状态。

  • boolean isClosed() 返回 ServerSocket 的关闭状态。

  • void setPerformancePreferences(int connectionTime, int latency, int bandwidth) 设置此 ServerSocket 的性能首选项。

  • void setReceiveBufferSize(int size) 为从此 ServerSocket 接受的套接字的 SO_RCVBUF 选项设置默认建议值。

  • void setReuseAddress(boolean on) 启用/禁用 SO_REUSEADDR 套接字选项。

  • static void setSocketFactory(SocketImplFactory fac) 为应用程序设置服务器套接字实现工厂。

  • void setSoTimeout(int timeout) 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。

  • String toString() 作为 String 返回此套接字的实现地址和实现端口。


Socket

构造方法


  • Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字

  • Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定IP地址的指定端口号

  • Socket(InetAddress address, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程地址上的指定远程端口。

  • Socket(Proxy proxy) 创建一个未连接的套接字并指定代理类型(如果有),该代理不管其他设置如何都应被使用。

  • Socket(SocketImpl impl) 使用用户指定的 SocketImpl 创建一个未连接 Socket。

  • Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。

  • Socket(String host, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程主机上的指定远程端口。

常用方法


  • void bind(SocketAddress bindpoint) 将套接字绑定到本地地址。

  • void close() 关闭此套接字。

  • void connect(SocketAddress endpoint) 将此套接字连接到服务器。

  • void connect(SocketAddress endpoint, int timeout) 将此套接字连接到服务器,并指定一个超时值

  • SocketChannel getChannel() 返回与此数据报套接字关联的唯一 SocketChannel 对象(如果有)。

  • InetAddress getInetAddress() 返回套接字连接的地址。

  • InputStream getInputStream() 返回此套接字的输入流。

  • boolean getKeepAlive() 测试是否启用 SO_KEEPALIVE。

  • InetAddress getLocalAddress() 获取套接字绑定的本地地址。

  • int getLocalPort() 返回此套接字绑定到的本地端口。

  • SocketAddress getLocalSocketAddress() 返回此套接字绑定的端点的地址,如果尚未绑定则返回 null。

  • boolean getOOBInline() 测试是否启用 OOBINLINE。

  • OutputStream getOutputStream() 返回此套接字的输出流。

  • int getPort() 返回此套接字连接到的远程端口。

  • int getReceiveBufferSize() 获取此 Socket 的 SO_RCVBUF 选项的值,该值是平台在 Socket 上输入时使用的缓冲区大小。

  • SocketAddress getRemoteSocketAddress() 返回此套接字连接的端点的地址,如果未连接则返回 null。

  • boolean getReuseAddress() 测试是否启用 SO_REUSEADDR。

  • int getSendBufferSize() 获取此 Socket 的 SO_SNDBUF 选项的值,该值是平台在 Socket 上输出时使用的缓冲区大小。

  • int getSoLinger() 返回 SO_LINGER 的设置。

  • int getSoTimeout() 返回 SO_TIMEOUT 的设置。

  • boolean getTcpNoDelay() 测试是否启用 TCP_NODELAY。

  • int getTrafficClass() 为从此 Socket 上发送的包获取 IP 头中的流量类别或服务类型。

  • boolean isBound() 返回套接字的绑定状态。

  • boolean isClosed() 返回套接字的关闭状态。

  • boolean isConnected() 返回套接字的连接状态。

  • boolean isInputShutdown() 返回是否关闭套接字连接的半读状态 (read-half) 。

  • boolean isOutputShutdown() 返回是否关闭套接字连接的半写状态 (write-half) 。

  • void sendUrgentData(int data) 在套接字上发送一个紧急数据字节。

  • void setKeepAlive(boolean on) 启用/禁用 SO_KEEPALIVE。

  • void setOOBInline(boolean on) 启用/禁用 OOBINLINE(TCP 紧急数据的接收者) 默认情况下,此选项是禁用的,即在套接字上接收的 TCP 紧急数据被静默丢弃。

  • void setPerformancePreferences(int connectionTime, int latency, int bandwidth) 设置此套接字的性能偏好。

  • void setReceiveBufferSize(int size) 将此 Socket 的 SO_RCVBUF 选项设置为指定的值。

  • void setReuseAddress(boolean on) 启用/禁用 SO_REUSEADDR 套接字选项。

  • void setSendBufferSize(int size) 将此 Socket 的 SO_SNDBUF 选项设置为指定的值。

  • static void setSocketImplFactory(SocketImplFactory fac) 为应用程序设置客户端套接字实现工厂。

  • void setSoLinger(boolean on, int linger) 启用/禁用具有指定逗留时间(以秒为单位)的 SO_LINGER。

  • void setSoTimeout(int timeout) 启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。

  • void setTcpNoDelay(boolean on) 启用/禁用 TCP_NODELAY(启用/禁用 Nagle 算法)。

  • void setTrafficClass(int tc) 为从此 Socket 上发送的包在 IP 头中设置流量类别 (traffic class) 或服务类型八位组 (type-of-service octet) 。

  • void shutdownInput() 此套接字的输入流置于“流的末尾”。

  • void shutdownOutput() 禁用此套接字的输出流。

  • String toString() 将此套接字转换为 String。

2018-11-23


推荐阅读
  • 本文内容为asp.net微信公众平台开发的目录汇总,包括数据库设计、多层架构框架搭建和入口实现、微信消息封装及反射赋值、关注事件、用户记录、回复文本消息、图文消息、服务搭建(接入)、自定义菜单等。同时提供了示例代码和相关的后台管理功能。内容涵盖了多个方面,适合综合运用。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • CentOS 6.5安装VMware Tools及共享文件夹显示问题解决方法
    本文介绍了在CentOS 6.5上安装VMware Tools及解决共享文件夹显示问题的方法。包括清空CD/DVD使用的ISO镜像文件、创建挂载目录、改变光驱设备的读写权限等步骤。最后给出了拷贝解压VMware Tools的操作。 ... [详细]
  • 移动端常用单位——rem的使用方法和注意事项
    本文介绍了移动端常用的单位rem的使用方法和注意事项,包括px、%、em、vw、vh等其他常用单位的比较。同时还介绍了如何通过JS获取视口宽度并动态调整rem的值,以适应不同设备的屏幕大小。此外,还提到了rem目前在移动端的主流地位。 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
  • 代理模式的详细介绍及应用场景
    代理模式是一种在软件开发中常用的设计模式,通过在客户端和目标对象之间增加一层中间层,让代理对象代替目标对象进行访问,从而简化系统的复杂性。代理模式可以根据不同的使用目的分为远程代理、虚拟代理、Copy-on-Write代理、保护代理、防火墙代理、智能引用代理和Cache代理等几种。本文将详细介绍代理模式的原理和应用场景。 ... [详细]
  • 本文介绍了互联网思维中的三个段子,涵盖了餐饮行业、淘品牌和创业企业的案例。通过这些案例,探讨了互联网思维的九大分类和十九条法则。其中包括雕爷牛腩餐厅的成功经验,三只松鼠淘品牌的包装策略以及一家创业企业的销售额增长情况。这些案例展示了互联网思维在不同领域的应用和成功之道。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • R语言openxlsx、car、rmarkdown包安装报错: 句法分析器2行里不能有多字节字符;解决WARNING: Rtools is required to build R packages
    每次打开Rstudio这里会警告句法分析器2行里不能有多字节字符当安装car包时报错,安装Markdown包一直加载不出来,查了一下安装上了Rtool ... [详细]
author-avatar
45度向上倾斜的世界取_872
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有