资料来源
android进阶3step2:Android App通信 ——端口号IP等网络基础知识扫盲
具体资料参考资料来源1
这里只记录练习过程
基本实现(单向获取客户端输入)
服务端实现
设置监听ip地址(host)和监听端口(ip)并实例化SocketServer
通过accept()方法,阻塞程序,等待socket连接并获取socket实例
通过getInputStream()获取Socket得输入流,处理输入流获取socket携带的信息
对获取信息进行处理
关闭资源(输入流,Socket,Server)
public class DemoServer {
public static void main(String[] args) throws IOException {
//默认本机ip 127.0.0.1,通信端口10000
int port = 10000;
//实例化server,对指定端口监听
System.out.println("初始化服务器...");
ServerSocket server = new ServerSocket(port);
//阻塞程序,等待socket连接
System.out.println("服务开始等待连接.....");
Socket socket = server.accept();
//获取输入流
System.out.println("获取输入流并处理...");
InputStream input = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
while ((len = input.read(bytes)) != -1) {
sb.append(new String(bytes, 0, len, "UTF-8"));
}
//输出获取的输入信息
System.out.println("输出获取的输入信息:message=" + sb.toString());
//关闭资源
System.out.println("关闭资源...");
input.close();
socket.close();
server.close();
}
}
客户端实现
根据host和port创建Socket连接
获取输出流并输出信息
关闭资源
public class DemoSocket {
public static void main(String[] args) throws IOException {
String ip="127.0.0.1";
int port=10000;
//建立连接
Socket socket=new Socket(ip,port);
//获取输出流,写入信息
OutputStream output=socket.getOutputStream();
String message="你好,这是是客户端";
output.write(message.getBytes("UTF-8"));
//关闭资源
output.close();
socket.close();
}
}
控制台输出
DemoSocket
建立Socket连接
写入输出流
关闭资源
DemoServer
初始化服务器...
服务开始等待连接.....
获取输入流并处理...
输出获取的输入信息:message=你好,这是是客户端
关闭资源...
服务器和客户端双向通信
服务器实现
在收到客户端信息后,返回信息给客户端
public class DemoServer {
public static void main(String[] args) throws IOException {
startServerDouble();
}
static void startServerDouble() throws IOException {
//默认本机ip 127.0.0.1,通信端口10000
int port = 10000;
//实例化server,对指定端口监听
System.out.println("初始化服务器...");
ServerSocket server = new ServerSocket(port);
//阻塞程序,等待socket连接
System.out.println("服务开始等待连接.....");
Socket socket = server.accept();
//获取输入流
System.out.println("获取输入流并处理...");
InputStream input = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
while ((len = input.read(bytes)) != -1) {
sb.append(new String(bytes, 0, len, "UTF-8"));
}
//输出获取的输入信息
System.out.println("输出获取的输入信息:message=" + sb.toString());
//服务器返回信息给客户端,注意统一编码格式
System.out.println("服务器返回信息给客户端");
OutputStream output=socket.getOutputStream();
String message="服务器已收到,请客户端放心";
output.write(message.getBytes("UTF-8"));
//关闭资源
System.out.println("关闭资源...");
input.close();
output.close();
socket.close();
server.close();
}
}
客户端实现
在发送信息后,告知服务器发送完毕,接收服务器信息并输出
public class DemoSocket {
public static void main(String[] args) throws IOException {
startSocketDouble();
}
static void startSocketDouble() throws IOException {
String ip="127.0.0.1";
int port=10000;
//建立连接
System.out.println("建立Socket连接");
Socket socket=new Socket(ip,port);
//获取输出流,写入信息
System.out.println("写入输出流");
OutputStream output=socket.getOutputStream();
String message="你好,这是是客户端";
output.write(message.getBytes("UTF-8"));
//告知服务器输入信息完毕
socket.shutdownOutput();
//获取输入流,并展示
System.out.println("获取服务器输入流");
InputStream input=socket.getInputStream();
StringBuilder sb=new StringBuilder();
int len;
byte[] bytes=new byte[1024];
while ((len=input.read(bytes))!=-1){
sb.append(new String(bytes,0,len,"UTF-8"));
}
System.out.println("服务器输入信息:message="+sb.toString());
//关闭资源
System.out.println("关闭资源");
output.close();
input.close();
socket.close();
}
}
控制台输出
DemoServer
初始化服务器...
服务开始等待连接.....
获取输入流并处理...
输出获取的输入信息:message=你好,这是是客户端
服务器返回信息给客户端
关闭资源...
DemoSocket
建立Socket连接
写入输出流
获取服务器输入流
服务器输入信息:message=服务器已收到,请客户端放心
关闭资源
优化1
如何让服务器和客户端知道什么时候结束输入开始输出,复用同一个socket.
默认soket.close(),或者input.close(),都会导致socket关闭,需要重复建立socket才能继续发送接受
发送完毕,socket.shutdownInputStream(),会告知服务器输入完毕,但是只能继续接受,无法继续发送,要发送信息,需要重新建立socket.
这里采用资源1中推荐的消息长度(两个字节)+消息内容的方式,来利用一个socket持续进行收发消息
单向发送消息
服务器实现
获取输入流后,首先读取两个字节作为长度信息,这里通过位运算符,来处理两个字节,合并为一个长度
public class DemoServer {
public static void main(String[] args) throws IOException {
startServerOneWithLength();
}
static void startServerOneWithLength() throws IOException {
//默认本机ip 127.0.0.1,通信端口10000
int port = 10000;
//实例化server,对指定端口监听
System.out.println("初始化服务器...");
ServerSocket server = new ServerSocket(port);
//阻塞程序,等待socket连接
System.out.println("服务开始等待连接.....");
Socket socket = server.accept();
//获取输入流
System.out.println("获取输入流并处理...");
InputStream input = socket.getInputStream();
while (true) {
//首先读取两个字节,获取长度
System.out.println("开始获取信息");
int fLengthOne = input.read();
if (fLengthOne == -1) {
System.out.println("获取客户端输入信息结束");
break;
}
int fLengthSecond = input.read();
int fLength = (fLengthOne << 8) + fLengthSecond;
byte[] bytes = new byte[fLength];
input.read(bytes);
//输出获取的输入信息
System.out.println("输出获取的输入信息:message=" + new String(bytes,"UTF-8"));
}
//关闭资源
System.out.println("关闭资源...");
input.close();
socket.close();
server.close();
}
}
客户端实现
发送信息时,首先计算消息长度,然后通过位运算符分割,先发送消息长度,再发送内容,然后通过flush()刷新并输出缓存区,接着发送下一个消息
public class DemoSocket {
public static void main(String[] args) throws IOException {
startSocketOneWithLength();
}
//单向通信Socket/约定两个字节作为长度
static void startSocketOneWithLength() throws IOException {
String ip="127.0.0.1";
int port=10000;
//建立连接
System.out.println("建立Socket连接");
Socket socket=new Socket(ip,port);
//获取输出流
System.out.println("获取输出流");
//写入信息1
System.out.println("写入信息1");
//计算长度,发送时放在前两个字节
OutputStream output=socket.getOutputStream();
String message="你好,这是是客户端";
byte[] bytes=message.getBytes("UTF-8");
int length=bytes.length;
output.write(length>>8);
output.write(length);
output.write(bytes);
output.flush();
//写入信息1
System.out.println("写入信息2");
//计算长度,发送时放在前两个字节
String message2="你好,服务器,这是第二条信息";
byte[] bytes2=message2.getBytes("UTF-8");
int length2=bytes2.length;
output.write(length2>>8);
output.write(length2);
output.write(bytes2);
output.flush();
//关闭资源
System.out.println("关闭资源");
output.close();
socket.close();
}
}
控制台输出
DemoServer
开始获取信息
输出获取的输入信息:message=你好,这是是客户端
开始获取信息
输出获取的输入信息:message=你好,服务器,这是第二条信息
开始获取信息
获取客户端输入信息结束
关闭资源...
DemoSocket
建立Socket连接
获取输出流
写入信息1
写入信息2
关闭资源
优化2
实际生产时,不会每次专门为一个socket启动一个服务,处理完socket再关闭.
服务器的实现需要加入多线程处理,线程管理通过线程池,指定线程池大小,超过限制,新的连接进入等待.
服务器实现改造
循环获取socket,并通过线程池分配线程执行
public class DemoServer {
public static void main(String[] args) throws IOException {
startServerWithThreadPool();
}
static void startServerWithThreadPool() throws IOException {
//默认本机ip 127.0.0.1,通信端口10000
int port = 10000;
//实例化server,对指定端口监听
System.out.println("初始化服务器...");
ServerSocket server = new ServerSocket(port);
//启动线程池,等待连接
System.out.println("服务开始等待连接.....");
ExecutorService threadPool=Executors.newFixedThreadPool(100);
//进入循环持续获取socket,并为获得的socket分配线程
while (true){
Socket socket = server.accept();
Runnable runnable=new Runnable() {
@Override
public void run() {
//获取输入流
System.out.println("获取输入流并处理...");
try {
InputStream input = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
while ((len = input.read(bytes)) != -1) {
sb.append(new String(bytes, 0, len, "UTF-8"));
}
//输出获取的输入信息
System.out.println("输出获取的输入信息:message=" + sb.toString());
//服务器返回信息给客户端,注意统一编码格式
System.out.println("服务器返回信息给客户端");
OutputStream output=socket.getOutputStream();
String message="服务器已收到,请客户端放心";
output.write(message.getBytes("UTF-8"));
//关闭资源
System.out.println("关闭资源...");
input.close();
output.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
};
threadPool.submit(runnable);
}
}
}
客户端实现
采用前文双向通讯实现
控制台输出
打开服务,调用两次socket客户端实现,连续获取两次输出
DemoServer
初始化服务器...
服务开始等待连接.....
获取输入流并处理...
输出获取的输入信息:message=你好,这是是客户端
服务器返回信息给客户端
关闭资源...
获取输入流并处理...
输出获取的输入信息:message=你好,这是是客户端
服务器返回信息给客户端
关闭资源...
ps:
inputStream的比较有意思的方法
input.available(),返回从上次read()的位置到下一次可read()的位置之间的内容长度.
实际使用中,BufferedInpuStream来替换InputStream来改善读写性能
DataInputStream用来获取原始数据.
Socket比较有趣的方法
//根据host,port创建socket
String ip="127.0.0.1";
int port=10000;
Socket socket=new Socket(ip,port);
//根据host,port和超时时间创建socket
InetSocketAddress address=new InetSocketAddress("127.0.0.1",10000);
socket.connect(address,10000);
//判断socket是否连接
socket.isConnected()