项目需求收集通过Socket向服务器发送图片,之前没搞过,网上搜搜写了下面的例子,勉强解决了需求。
为了测试切换着方便,所以方法写的有点碎了。。。
原文地址 http://blog.csdn.net/qq_25806863/article/details/75533109
要求发送的消息的格式是,8个字节的消息长度+消息体
因为需要8个字节,所以消息长度决定用long
如果需要4个字节,可以用int
。
手机客户端接收服务器的文字消息
服务端
服务端定义好端口号,开启以一个ServerSocket,写入文字消息:
public class Service {
private static Socket socket;
//定义端口号
private static final int POST = 30000;
public static void main(String[] args) {
try {
//发送的内容
sendMsg("来自服务器的问候");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void sendMsg(String msg) throws IOException {
System.out.println("开始连接");
//创建socket服务端口是30000,并等待连接
ServerSocket serverSocket = new ServerSocket(POST);
Socket socket = serverSocket.accept();
//获取输出流
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
sendTextMsg(out, msg);
out.close();
socket.close();
serverSocket.close();
System.out.println("通讯结束");
}
public static void sendTextMsg(DataOutputStream out, String msg) throws IOException {
//先写长度,就是消息体的字节数,long刚好8个字节
out.writeLong(msg.getBytes().length);
//写入消息
out.write(msg.getBytes());
}
}
客户端,接收消息
客户端首先要跟服务器进行连接,然后才能进行通讯。
Socket连接需要网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
因为属于网络通讯,所以socket连接也不能放在主线程中,否则会报错
-
添加按钮
在布局中加一个按钮,点击方法是receive
<Button android:text="接收" android:layout_marginTop="50dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="receive"/>
-
定义方法
在Activity中定义方法
因为不能再主线程中访问,所以需要子线程。这里直接new了、、
public void receive(View view){ new Thread(new Runnable() { @Override public void run() { Socket socket; try { //这里进行连接服务器, //host是服务器ip地址,如“192.168.2.12” //post是端口,上面的服务端提供的端口号是30000 socket = new Socket(host, post); //获取输入流 DataInputStream input = new DataInputStream(socket.getInputStream()); //读取长度,也即是消息头, long len = input.readLong(); //创建这个长度的字节数组 byte[] bytes = new byte[(int)len]; //再读取这个长度的字节数,也就是真正的消息体 input.read(bytes); //将字节数组转为String String s = new String(bytes); Log.i("accept", "len: "+len); Log.i("accept", "msg: "+s); } catch (IOException e) { e.printStackTrace(); } } }).start(); }
运行测试
先运行服务端,会发现程序没有一次执行完,在阻塞着等待连接
这时点击app中的接收按钮,日志中会打印接收到的信息
长度为24,消息内容为“来自服务器的问候”。
因为在utf-8中一个汉字是3个字节,所以8个汉字的消息长度是24字节。
这是看服务器端的打印:
发现服务端也正常执行完毕了。
手机客户端向服务端发送文字消息
服务端接收消息
public class Service {
private static Socket socket;
//定义端口号
private static final int POST = 30000;
public static void main(String[] args) {
try {
//接收消息
getMsg();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void getMsg() throws IOException {
System.out.println("开始连接");
ServerSocket serverSocket = new ServerSocket(POST);
Socket socket = serverSocket.accept();
//获取输入流,通过这个流来读取消息
DataInputStream input = new DataInputStream(socket.getInputStream());
//接收文字消息
getTextMsg(input);
input.close();
socket.close();
serverSocket.close();
System.out.println("通讯结束");
}
public static String getTextMsg(DataInputStream input) throws IOException {
//一样先读长度,再根据长度读消息
long len = input.readLong();
System.out.println("len = " + len);
byte[] bytes = new byte[(int) len];
input.read(bytes);
String msg = new String(bytes);
System.out.println("msd = " + msg);
return msg;
}
}
客户端发送消息
- 增加一个发送的按钮:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送!"
android:onClick="send"/>
- 定义方法
public void send(View view) {
new Thread(new Runnable() {
@Override
public void run() {
Socket socket;
try {
//建立连接
socket = new Socket(host, post);
//获取输出流,通过这个流发送消息
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
//发送文字消息
sendTextMsg(out,"来自手机客户端的消息");
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
public void sendTextMsg(DataOutputStream out, String msg) throws IOException {
byte[] bytes = msg.getBytes();
long len = bytes.length;
//先发送长度,在发送内容
out.writeLong(len);
out.write(bytes);
}
运行测试
先运行服务端,也会停留着这个界面等待连接:
然后点击客户端的发送按钮,这是服务端会变成下面这样,完成这次消息的通讯
手机客户端想服务端发送图片
服务端接收图片,并保存到本地
在上面的getMsg()
方法中,将getTextMsg(input);
改为getImgMsg(input);
下面是getImgMsg(input);
方法:
public static void getImgMsg(DataInputStream input) throws IOException {
//同样是先读长度
long len = input.readLong();
System.out.println("len = " + len);
byte[] bytes = new byte[(int) len];
//然后在读这个长度的字节到字节数组
input.readFully(bytes);
//将独到的内容保存为文件到本地
File file = new File("/Users/xxx/" + len + ".png");
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(bytes);
System.out.println("ok");
}
客户端发送图片
- 增加一个按钮:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="200dp"
android:onClick="sendImg"
android:text="发送图片" />
-
定义方法
public void sendImgMsg(DataOutputStream out ) throws IOException { //发送的图片为图标,就是安卓机器人,将bitmap转为字节数组 Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round); ByteArrayOutputStream bout = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG,100,bout); //写入字节的长度,再写入图片的字节 long len = bout.size(); //这里打印一下发送的长度 Log.i("sendImgMsg", "len: "+len); out.writeLong(len); out.write(bout.toByteArray()); }
运行测试
还是先开启服务端,服务器变成:
然后点击app的发送图片按钮,app中打印日志:
说明发送的图片的长度是11418个字节,大致换算大小是11k。
然后看服务端的日志:
接收到的长度也是11418,并且保存到了文件,
关于心跳包
上面的例子中,每发送一次之后就把链接关闭了:
out.close();
socket.close();
serverSocket.close();
心跳其实就是定期向服务端发送一个小数据,比如0.
让服务器知道这个链接还有用,不用关闭。
简单实现起来就是客户端通过一个无限循环,不停地向服务端发送消息,服务端通过一个无限循环不停地接收消息,都不关闭这个链接就行了。
服务端
public static void main(String[] args) {
try {
System.out.println("开始接收信息");
ServerSocket serverSocket = new ServerSocket(POST);
socket = serverSocket.accept();
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
long len = 0;
len = dataInputStream.readLong();
System.out.println("len = " + len);
byte[] bytes = new byte[(int) len];
dataInputStream.readFully(bytes);
String s = new String(bytes);
System.out.println("data = " + s);
} catch (IOException e) {
e.printStackTrace();
isConnect = false;
}
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
客户端,一秒发送一次消息
try {
//建立一次链接
socket = new Socket(host,post);
outputStream = new DataOutputStream(socket.getOutputStream());
inputStream = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
Log.i(TAG, "run: 开始循环发送发送心跳");
//一秒发送一个0,
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "run: 发送心跳0");
try {
outputStream.writeLong("0".getBytes().length);
outputStream.write("0".getBytes());
} catch (IOException e) {
e.printStackTrace();
isConnect = false;
}
}
运行测试
先运行服务端:
然后点击发送心跳的按钮,app的日志中打印:
在服务端的日志中可以看到:
一秒一次