項(xiàng)目需求收集通過Socket向服務(wù)器發(fā)送圖片,之前沒搞過,網(wǎng)上搜搜寫了下面的例子,勉強(qiáng)解決了需求。
為了測(cè)試切換著方便,所以方法寫的有點(diǎn)碎了。。。
原文地址 http://blog.csdn.net/qq_25806863/article/details/75533109
要求發(fā)送的消息的格式是,8個(gè)字節(jié)的消息長(zhǎng)度+消息體

因?yàn)樾枰?個(gè)字節(jié),所以消息長(zhǎng)度決定用long
如果需要4個(gè)字節(jié),可以用int。
手機(jī)客戶端接收服務(wù)器的文字消息
服務(wù)端
服務(wù)端定義好端口號(hào),開啟以一個(gè)ServerSocket,寫入文字消息:
public class Service {
private static Socket socket;
//定義端口號(hào)
private static final int POST = 30000;
public static void main(String[] args) {
try {
//發(fā)送的內(nèi)容
sendMsg("來自服務(wù)器的問候");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void sendMsg(String msg) throws IOException {
System.out.println("開始連接");
//創(chuàng)建socket服務(wù)端口是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("通訊結(jié)束");
}
public static void sendTextMsg(DataOutputStream out, String msg) throws IOException {
//先寫長(zhǎng)度,就是消息體的字節(jié)數(shù),long剛好8個(gè)字節(jié)
out.writeLong(msg.getBytes().length);
//寫入消息
out.write(msg.getBytes());
}
}
客戶端,接收消息
客戶端首先要跟服務(wù)器進(jìn)行連接,然后才能進(jìn)行通訊。
Socket連接需要網(wǎng)絡(luò)權(quán)限:
<uses-permission android:name="android.permission.INTERNET"/>
因?yàn)閷儆诰W(wǎng)絡(luò)通訊,所以socket連接也不能放在主線程中,否則會(huì)報(bào)錯(cuò)

-
添加按鈕
在布局中加一個(gè)按鈕,點(diǎn)擊方法是receive
<Button android:text="接收" android:layout_marginTop="50dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="receive"/> -
定義方法
在Activity中定義方法
因?yàn)椴荒茉僦骶€程中訪問,所以需要子線程。這里直接new了、、
public void receive(View view){ new Thread(new Runnable() { @Override public void run() { Socket socket; try { //這里進(jìn)行連接服務(wù)器, //host是服務(wù)器ip地址,如“192.168.2.12” //post是端口,上面的服務(wù)端提供的端口號(hào)是30000 socket = new Socket(host, post); //獲取輸入流 DataInputStream input = new DataInputStream(socket.getInputStream()); //讀取長(zhǎng)度,也即是消息頭, long len = input.readLong(); //創(chuàng)建這個(gè)長(zhǎng)度的字節(jié)數(shù)組 byte[] bytes = new byte[(int)len]; //再讀取這個(gè)長(zhǎng)度的字節(jié)數(shù),也就是真正的消息體 input.read(bytes); //將字節(jié)數(shù)組轉(zhuǎn)為String String s = new String(bytes); Log.i("accept", "len: "+len); Log.i("accept", "msg: "+s); } catch (IOException e) { e.printStackTrace(); } } }).start(); }
運(yùn)行測(cè)試
先運(yùn)行服務(wù)端,會(huì)發(fā)現(xiàn)程序沒有一次執(zhí)行完,在阻塞著等待連接

這時(shí)點(diǎn)擊app中的接收按鈕,日志中會(huì)打印接收到的信息

長(zhǎng)度為24,消息內(nèi)容為“來自服務(wù)器的問候”。
因?yàn)樵趗tf-8中一個(gè)漢字是3個(gè)字節(jié),所以8個(gè)漢字的消息長(zhǎng)度是24字節(jié)。
這是看服務(wù)器端的打?。?/p>

發(fā)現(xiàn)服務(wù)端也正常執(zhí)行完畢了。
手機(jī)客戶端向服務(wù)端發(fā)送文字消息
服務(wù)端接收消息
public class Service {
private static Socket socket;
//定義端口號(hào)
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();
//獲取輸入流,通過這個(gè)流來讀取消息
DataInputStream input = new DataInputStream(socket.getInputStream());
//接收文字消息
getTextMsg(input);
input.close();
socket.close();
serverSocket.close();
System.out.println("通訊結(jié)束");
}
public static String getTextMsg(DataInputStream input) throws IOException {
//一樣先讀長(zhǎng)度,再根據(jù)長(zhǎng)度讀消息
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;
}
}
客戶端發(fā)送消息
- 增加一個(gè)發(fā)送的按鈕:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="發(fā)送!"
android:onClick="send"/>
- 定義方法
public void send(View view) {
new Thread(new Runnable() {
@Override
public void run() {
Socket socket;
try {
//建立連接
socket = new Socket(host, post);
//獲取輸出流,通過這個(gè)流發(fā)送消息
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
//發(fā)送文字消息
sendTextMsg(out,"來自手機(jī)客戶端的消息");
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;
//先發(fā)送長(zhǎng)度,在發(fā)送內(nèi)容
out.writeLong(len);
out.write(bytes);
}
運(yùn)行測(cè)試
先運(yùn)行服務(wù)端,也會(huì)停留著這個(gè)界面等待連接:

然后點(diǎn)擊客戶端的發(fā)送按鈕,這是服務(wù)端會(huì)變成下面這樣,完成這次消息的通訊

手機(jī)客戶端想服務(wù)端發(fā)送圖片
服務(wù)端接收?qǐng)D片,并保存到本地
在上面的getMsg()方法中,將getTextMsg(input);改為getImgMsg(input);
下面是getImgMsg(input);方法:
public static void getImgMsg(DataInputStream input) throws IOException {
//同樣是先讀長(zhǎng)度
long len = input.readLong();
System.out.println("len = " + len);
byte[] bytes = new byte[(int) len];
//然后在讀這個(gè)長(zhǎng)度的字節(jié)到字節(jié)數(shù)組
input.readFully(bytes);
//將獨(dú)到的內(nèi)容保存為文件到本地
File file = new File("/Users/xxx/" + len + ".png");
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(bytes);
System.out.println("ok");
}
客戶端發(fā)送圖片
- 增加一個(gè)按鈕:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="200dp"
android:onClick="sendImg"
android:text="發(fā)送圖片" />
-
定義方法
public void sendImgMsg(DataOutputStream out ) throws IOException { //發(fā)送的圖片為圖標(biāo),就是安卓機(jī)器人,將bitmap轉(zhuǎn)為字節(jié)數(shù)組 Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round); ByteArrayOutputStream bout = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG,100,bout); //寫入字節(jié)的長(zhǎng)度,再寫入圖片的字節(jié) long len = bout.size(); //這里打印一下發(fā)送的長(zhǎng)度 Log.i("sendImgMsg", "len: "+len); out.writeLong(len); out.write(bout.toByteArray()); }
運(yùn)行測(cè)試
還是先開啟服務(wù)端,服務(wù)器變成:

然后點(diǎn)擊app的發(fā)送圖片按鈕,app中打印日志:

說明發(fā)送的圖片的長(zhǎng)度是11418個(gè)字節(jié),大致?lián)Q算大小是11k。
然后看服務(wù)端的日志:

接收到的長(zhǎng)度也是11418,并且保存到了文件,

關(guān)于心跳包
上面的例子中,每發(fā)送一次之后就把鏈接關(guān)閉了:
out.close();
socket.close();
serverSocket.close();
心跳其實(shí)就是定期向服務(wù)端發(fā)送一個(gè)小數(shù)據(jù),比如0.
讓服務(wù)器知道這個(gè)鏈接還有用,不用關(guān)閉。
簡(jiǎn)單實(shí)現(xiàn)起來就是客戶端通過一個(gè)無限循環(huán),不停地向服務(wù)端發(fā)送消息,服務(wù)端通過一個(gè)無限循環(huán)不停地接收消息,都不關(guān)閉這個(gè)鏈接就行了。
服務(wù)端
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();
}
}
客戶端,一秒發(fā)送一次消息
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: 開始循環(huán)發(fā)送發(fā)送心跳");
//一秒發(fā)送一個(gè)0,
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.i(TAG, "run: 發(fā)送心跳0");
try {
outputStream.writeLong("0".getBytes().length);
outputStream.write("0".getBytes());
} catch (IOException e) {
e.printStackTrace();
isConnect = false;
}
}
運(yùn)行測(cè)試
先運(yùn)行服務(wù)端:

然后點(diǎn)擊發(fā)送心跳的按鈕,app的日志中打?。?/p>

在服務(wù)端的日志中可以看到:

一秒一次