服务端Java代码:
public class SocketDemo {
private static final int SOCKET_PORT = 50000;
public static ArrayList<Socket> socketList = new ArrayList<Socket>();
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(SOCKET_PORT);
while (true) {
Socket socket = serverSocket.accept();
socketList.add(socket);
new Thread(new SocketThread(socket)).start();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (serverSocket != null) {
serverSocket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class SocketThread implements Runnable {
private Socket socket = null;
private BufferedReader br = null;
private BufferedWriter bw=null;
public SocketThread(Socket socket) {
this.socket = socket;
}
public void run() {
try {
while(true){
String shared_content = "";
StringBuffer buffer = new StringBuffer();
if(br==null){
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
String content;
while (!"END".equals(content = br.readLine())) {
buffer.append(content);
}
for (Socket mSocket : SocketDemo.socketList) {
bw = new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream()));
shared_content = packageMessage(buffer.toString());
bw.write(shared_content);
bw.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public String packageMessage(String msg) {
String message = "";
if (msg.startsWith("1")) {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
String time = format.format(date);
message = message + "user1 " + time + " :" + msg.substring(1)+"\r\n"+"END\r\n";
}
if (msg.startsWith("2")) {
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
String time = format.format(date);
message = message + "user2 " + time + " :" + msg.substring(1)+"\r\n"+"END\r\n";
}
return message;
}
}
android客户端代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
public static final int PORT=50000;
public static final String inetAddress="10.0.2.2";
private EditText mEditText;
private Button mButton1;
private Button mButton2;
private LinearLayout mLinearLayout1;
private LinearLayout mLinearLayout2;
private Socket socket1=null;
private Socket socket2=null;
private PrintWriter pw1=null;
private PrintWriter pw2=null;
private ClientThreadWriter clientThreadWriter1=null;
private ClientThreadWriter clientThreadWriter2=null;
private BufferedReader br1=null;
private BufferedReader br2=null;
private ClientThreadReader clientThreadReader1=null;
private ClientThreadReader clientThreadReader2=null;
private Handler handler=new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case 0:
mEditText.setText("");
break;
case 1:
TextView mTextView1=new TextView(MainActivity.this);
mTextView1.setText(msg.getData().getString("new_message"));
mLinearLayout1.addView(mTextView1);
break;
case 2:
TextView mTextView2=new TextView(MainActivity.this);
mTextView2.setText(msg.getData().getString("new_message"));
mLinearLayout2.addView(mTextView2);
break;
}
}
};
public void init(){
mEditText= (EditText) findViewById(R.id.edit_text);
mButton1= (Button) findViewById(R.id.button_1);
mButton2= (Button) findViewById(R.id.button_2);
mButton1.setOnClickListener(this);
mButton2.setOnClickListener(this);
mLinearLayout1= (LinearLayout) findViewById(R.id.record_1);
mLinearLayout2= (LinearLayout) findViewById(R.id.record_2);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
public void onClick(View view){
switch(view.getId()){
case R.id.button_1:
if(clientThreadWriter1==null){
clientThreadWriter1=new ClientThreadWriter(1);
clientThreadReader1=new ClientThreadReader(1);
new Thread(clientThreadReader1).start();
}
new Thread(clientThreadWriter1).start();
break;
case R.id.button_2:
if(clientThreadWriter2==null){
clientThreadWriter2=new ClientThreadWriter(2);
clientThreadReader2=new ClientThreadReader(2);
new Thread(clientThreadReader2).start();
}
new Thread(clientThreadWriter2).start();
break;
}
}
class ClientThreadWriter implements Runnable{
private int userId;
public ClientThreadWriter(int user){
userId=user;
}
public void run(){
try{
if(userId==1){
if(socket1==null){
//保证无论是user1还是user2都只持有一个socket,一个PrintWriter
socket1=new Socket(inetAddress,PORT);
pw1=new PrintWriter(new OutputStreamWriter(socket1.getOutputStream()));
br1=new BufferedReader(new InputStreamReader(socket1.getInputStream()));
}
pw1.write(userId+mEditText.getText().toString()+"\r\n"+"END\r\n");
pw1.flush();
handler.sendEmptyMessage(0);
}
if(userId==2){
if(socket2==null){
//保证无论是user1还是user2都只持有一个socket,一个PrintWriter
socket2=new Socket(inetAddress,PORT);
pw2=new PrintWriter(new OutputStreamWriter(socket2.getOutputStream()));
br2=new BufferedReader(new InputStreamReader(socket2.getInputStream()));
}
pw2.write(userId+mEditText.getText().toString()+"\r\n"+"END\r\n");
pw2.flush();
handler.sendEmptyMessage(0);
}
/*socket.shutdownOutput();*/
}catch(Exception e){
e.printStackTrace();
}
}
}
class ClientThreadReader implements Runnable{
private int userId;
public ClientThreadReader(int user){
userId=user;
}
public void run(){
try{
if(userId==1){
while(true){
if(socket1!=null){
String content;
StringBuffer buffer=new StringBuffer();Log.d("TAG123","reading");
while(!"END".equals(content=br1.readLine())){
buffer.append(content);
}
Message msg=Message.obtain();Log.d("TAG123",buffer.toString());
Bundle bundle=new Bundle();
bundle.putString("new_message",buffer.toString());
msg.setData(bundle);
msg.what=1;
handler.sendMessage(msg);
}
}
}
if(userId==2){
while(true){
if(socket2!=null){
String content;
StringBuffer buffer=new StringBuffer();
while(!"END".equals(content=br2.readLine())){
buffer.append(content);
}
Message msg=Message.obtain();
Bundle bundle=new Bundle();
bundle.putString("new_message",buffer.toString());
msg.setData(bundle);
msg.what=2;
handler.sendMessage(msg);
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}
运行效果:
编写过程中遇见的问题:
BufferedReader的readLine方法,InputStream的read方法都是阻塞方法,服务端建立输入流读取客户端的PrintWriter输出流,当客户端未输入任何内容时,服务端的readLine并不会返回null,而是出于阻塞状态(阻塞线程)。当客户端输入内容后,若输入内容不能填满PrintWriter的缓冲区,且没有用flush刷新缓冲区,那么服务端readLine不会读取到数据,依旧会阻塞线程。当使用flush方法刷新缓冲区后,服务端while((content=br.readLine())!=null){.....}仍然会阻塞线程,通过在数据后面添加“\r\n”能够解除readLine的线程阻塞,返回读取到的String,但是下一轮循环继续读取流,流中没有数据,readLine继续阻塞线程。最后自定义了一个结束符“END\r\n”,且修改了while循环while (!"END".equals(content = br.readLine())){......},当读取到"END\r\n"时readLine返回“END”,使得while循环为false,退出循环。解决BufferedReader readLine阻塞线程问题。
Demo整体思路:
客户端通过单击button开一个子线程去初始化socket,建立一个输出流,一个输入流,再开一个无限循环的子线程,不断读取来自服务端的数据。此后每次单击就仅单开一个线程向服务端输出数据即可。服务端通过ServerSocket.accept接收Socket,并开启一个无限循环的子线程去建立一个输入流,读取客户端的数据,再与服务端接收到的所有socket建立输出流,向客户端输出数据。