上一篇文章记录的是最基础的Socket和服务端连接,这篇文章记录的内容是:客户端与服务端相互传输数据的过程,其中客户端为安卓。如有不正确(有争议)的地方希望大家予以指正。
服务端如题使用Java编写,继续使用上一篇文章的部分代码。
客户端为Java,同时将Message封装为了一个Bean。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.demo.imudges.socketdemo.MainActivity" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="服务器说:"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/tv_server_msg" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="我给服务器说:"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/et_cilent_send_msg" /> </LinearLayout> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="发送信息" android:gravity="center" android:id="@+id/btn_send_msg" /> </LinearLayout>Message.class
package com.demo.imudges.socketdemo; import android.content.Context; import android.widget.Toast; import java.io.*; import java.net.Socket; public class Client { private Socket socket; private PrintWriter printWriter;//输出 private BufferedReader bufferedReader; private static String URL = "183.175.12.168"; private static int port = 6666; public String msg = ""; private Context context; public Client(Context context) { this.context = context; //设置服务器IP,绑定端口 try { socket = new Socket(URL,port); System.out.println("连接完成!"); } catch (IOException e) { e.printStackTrace(); } } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public void sendMessage(){ try { //向服务器发送数据 //初始化输出流 用来向服务器传递数据 if(socket.isOutputShutdown()){ System.err.println("OutputStream 被关闭"); } printWriter = new PrintWriter(socket.getOutputStream(),true); printWriter.println(msg); //清空缓冲区的数据流 数据流向:内存->缓冲区->文件(或输出),如果不用.flush(),可能缓冲区内部还有数据残留,.flush()会将缓冲区内部的数据强制输出 printWriter.flush(); } catch (IOException e) { e.printStackTrace(); } } public interface Listener{ void update(String msg); } private Listener listener; public void setListener(Listener l){ this.listener = l; } public void getServerMsg(){ //接收服务器数据 //初始化输入流 用来获取服务器下发的数据 try { bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String reply = null; while(!((reply = bufferedReader.readLine()) ==null)){ System.out.println("服务器发送的数据为:" + reply); listener.update(reply); } } catch (IOException e) { e.printStackTrace(); } } }MainActivity.class
package com.demo.imudges.socketdemo; import Bean.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.google.gson.Gson; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private TextView tvServerMsg; private EditText etCLientSendMsg; private Button btnSendMsg; private TelephonyManager telephonyManager; private String IMEI = ""; private Client client; private Message message; private Gson gson = new Gson(); /** * 初始化控件 * */ private void initViews(){ etCLientSendMsg = (EditText) findViewById(R.id.et_cilent_send_msg); tvServerMsg = (TextView) findViewById(R.id.tv_server_msg); btnSendMsg = (Button) findViewById(R.id.btn_send_msg); btnSendMsg.setOnClickListener(this); telephonyManager = (TelephonyManager) this.getSystemService(this.TELEPHONY_SERVICE); //获取设备唯一ID IMEI = telephonyManager.getDeviceId(); } /** * 初始化事件 * */ private void initEvents(){ //判断是否获取到IMEI if(IMEI != null && !IMEI.equals("")){ new Thread(new Runnable() { @Override public void run() { client = new Client(MainActivity.this); client.setListener(new Client.Listener() { @Override public void update(final String msg) { if(msg!=null){ //子线程中更新界面 new Thread(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { //Message messageFromServer = gson.fromJson(msg,Message.class); tvServerMsg.setText(msg); } }); } }).start(); } } }); client.getServerMsg(); } }).start(); message = new Message(); message.setClientID(IMEI); } else { Toast.makeText(this,"获取IMEI失败",Toast.LENGTH_SHORT).show(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); initEvents(); } @Override public void onClick(View view) { if(!TextUtils.isEmpty(etCLientSendMsg.getText())){ message.setMsg(etCLientSendMsg.getText().toString()); String msg = gson.toJson(message); client.setMsg(msg); new Thread(new Runnable() { @Override public void run() { client.sendMessage(); } }).start(); } else { } } }Client.class
package com.demo.imudges.socketdemo; import android.content.Context; import android.widget.Toast; import java.io.*; import java.net.Socket; public class Client { private Socket socket; private PrintWriter printWriter;//输出 private BufferedReader bufferedReader; private static String URL = "183.175.12.168"; private static int port = 6666; public String msg = ""; private Context context; public Client(Context context) { this.context = context; //设置服务器IP,绑定端口 try { socket = new Socket(URL,port); System.out.println("连接完成!"); } catch (IOException e) { e.printStackTrace(); } } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public void sendMessage(){ try { //向服务器发送数据 //初始化输出流 用来向服务器传递数据 if(socket.isOutputShutdown()){ System.err.println("OutputStream 被关闭"); } printWriter = new PrintWriter(socket.getOutputStream(),true); printWriter.println(msg); //清空缓冲区的数据流 数据流向:内存->缓冲区->文件(或输出),如果不用.flush(),可能缓冲区内部还有数据残留,.flush()会将缓冲区内部的数据强制输出 printWriter.flush(); } catch (IOException e) { e.printStackTrace(); } } public interface Listener{ void update(String msg); } private Listener listener; public void setListener(Listener l){ this.listener = l; } public void getServerMsg(){ //接收服务器数据 //初始化输入流 用来获取服务器下发的数据 try { bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String reply = null; while(!((reply = bufferedReader.readLine()) ==null)){ System.out.println("服务器发送的数据为:" + reply); listener.update(reply); } } catch (IOException e) { e.printStackTrace(); } } }贴上一张在网上找到的图片,个人觉得对于理解服务端和客户端Socket工作原理有所帮助。
最后总结一下我所踩的坑,希望大家引以为戒: 安卓端对于新开的线程,要用.start()方法启动,而不是.run()。 对于Socket接受和发送数据,无论是socket.shutdownInput() socket.shutdownOutput()还是socket.close()他们都会将Socket关闭,所以我使用printWriter.println()发送数据,使用bufferReader.readLint()接受数据,酱紫每次发送一行,每次读取一行,有明确的结束。同时不会使Scoket断开。(下面简单说说其他实现的方法) 2.1 使用Byte数组发送数据,每次发送的数据的第一位是数据长度,读取方只需要按照发送方给予的长度进行读取就可以,酱紫也不会使Socket断开。 2.2 发完就断开,断开重新连接。 理清楚思路再写代码,不要让服务端和客户端同时等待输入或者是同时输出。酱紫会造成死循环? socket.shutdownOutput() socket.shutdownInput() 是单向关闭流。