項目需求收集通過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的日志中打印:
在服務端的日志中可以看到:
一秒一次