Android socket 实现 wify 通信,简易聊天室 (一)

时间:2022-05-30 10:07:51

功能简介:

实现移动端Client连接上PC的wify后,实现与PC 上运行的Server进行简单的通信.

基础知识:

1)java 多线程机制

2)Handler消息传递机制

3)socket

4)Printstream,Bufferreader,readline()

5)Android 简单使用

代码:

服务器:

//MyServer.java

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

import org.omg.CORBA.SystemException;

public class MyServer {

private static final int PORT = 5000; //spy on the port 5000

void initMyserver() {
try {
ServerSocket serverSocket = new ServerSocket(PORT);

while(true) {
System.out.println("available");

//If there is not connect,it will block.
//Listen to the connect from client.
//If there is a connect ,it will return a socket.
Socket socket = serverSocket.accept();

//build a thread to deal with the socket
Thread thread = new Thread(new ServerThread(socket));
thread.start();
}
} catch (IOException e) {
// TODO: handle exception
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MyServer myServer = new MyServer();
myServer.initMyserver();
}

}

//ServerThread.java

import java.awt.print.PrinterGraphics;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.Buffer;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ServerThread implements Runnable {

Socket socket;
PrintStream output;
BufferedReader in;


public ServerThread(Socket socket) throws IOException {
this.socket = socket;
}

//broadcast the message from one client to every clients
public void run() {

String buffer;
try {

in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
output = new PrintStream(socket.getOutputStream());

buffer = ""; //if init buffer = null, will get a string which like "null..."
String line = null;

//Read message From readline.
//Here , You must be careful, because readline() will block rather than return null
//if there is no input.Readline() will return when you close the stream,such as
//socket.shutdownOutput();
while ((line = in.readLine()) != null) {
buffer = buffer + line;
//System.out.println(buffer);
output.println(packetMessage(buffer));
output.flush();
}

//You can replace the code of "Read message From readline " by follow,
//but you can not output to client after you shutdown output.
//while((line = in.readLine()) != null) {
//buffer = buffer + line;
//}
//output.println(packetMessage(buffer));
//output.flush();
// socket.shutdownOutput();

//System.out.println(buffer);

} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}

public String packetMessage(String buffer) {
String result = null;
SimpleDateFormat dFormat = new SimpleDateFormat("HH:mm:ss");
result = "Form server: "+dFormat.format(new Date())+" "+buffer;
return result;
}
}

客户端代码:

package com.example.yifeihappy.talkingroom;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.concurrent.TimeoutException;

public class MainActivity extends AppCompatActivity {

private String HOST = "125.216.250.3";//computer IP
private int PORT = 5000; //server PORT,the same to server
private String buffer;
PrintStream output;
BufferedReader in;
EditText edtMessage;
TextView tvContent;
Button btnSend;
Socket socket;
MyHandler myHandler = new MyHandler();

class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData();
String buffer = bundle.getString("newcontent");
tvContent.append('\n' + buffer);

}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

tvContent = (TextView)findViewById(R.id.content);
btnSend = (Button)findViewById(R.id.send);
edtMessage = (EditText)findViewById(R.id.message);

btnSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

String str = edtMessage.getText().toString();
new ClientThread(str).start();

}
});


}

class ClientThread extends Thread {
String str;

public ClientThread (String str) {
this.str = str;
}

@Override
public void run() {


Message ms = myHandler.obtainMessage();
Bundle bundle = new Bundle();
bundle.clear();



//build socket conncetion.
try {
socket = new Socket();
socket.connect(new InetSocketAddress(HOST,PORT),5000);

in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
output = new PrintStream(socket.getOutputStream());

output.println(str);
output.flush();

String line = null;
buffer = "";
while ((line = in.readLine()) != null) {
buffer = buffer + line;
if(buffer!=null) {
bundle.putString("newcontent",buffer);
ms.setData(bundle);
myHandler.sendMessage(ms);
}
}
}
catch (SocketTimeoutException aa)
{
bundle.putString("newcontent","There is Timeout");
ms.setData(bundle);
myHandler.sendMessage(ms);

}
catch (IOException e) {

bundle.putString("newcontent","There is IOEX");
ms.setData(bundle);
myHandler.sendMessage(ms);

}

try {

output.close();
in.close();
socket.close();
}
catch (IOException e) {
bundle.putString("newcontent","There is IOEX close");
ms.setData(bundle);
myHandler.sendMessage(ms);

}
}

}

}

注意:

要在 AndroidManifest.xml 下添加用户权限:

<application>
...
</application>
<uses-permission android:name="android.permission.INTERNET"/>

运行:

这里我把server建立在eclipse,将Myserver项目当做java applications 运行.

逻辑思路是:运行server,用于监听client的请求.如果没有请求,则在accept()处阻塞等待请求.当有Client建立链接时则产生一个socket,并新建一个线程与Client交互.

具体的交互为建立socket链接后Server在readline()处等待Client的输入并阻塞,Client有消息传递时处理并及时返回.

用户通过EditText输入并点击send Button,这时创建一个线程,在线程中产生一个socket与Server链接.Client向Server发送一个数据,并在发送一个数据后由于readline()无数据输入而阻塞,等待从Server处获取数据,在取得Server返回的数据后利用Handler处理UI,显示返回结果.



注意:

手机连接笔基本的wify,

代码中HOST为你电脑IP.

效果图:

Android socket 实现 wify 通信,简易聊天室 (一)

常见问题与解决:

Problems:Client 无法从Server 获取数据 或 Server无法获取Client数据

Analyse:很有可能是没有了解readline()的使用方法造成的问题.

这里要记住:readline()不是当没有输入的时候返回null.readline()当没有数据时会阻塞,在数据流异常或断开时才会返回null.

例如:把Client的原代码

 <span style="font-size:14px;">               output.println(str);
output.flush();

String line = null;
buffer = "";
while ((line = in.readLine()) != null) {
buffer = buffer + line;
if(buffer!=null) {
bundle.putString("newcontent",buffer);
ms.setData(bundle);
myHandler.sendMessage(ms);
}
}</span>
改为

<span style="font-size:14px;">                output.println(str);
output.flush();

String line = null;
buffer = "";
while ((line = in.readLine()) != null) {
buffer = buffer + line;
}</span>
}

<pre name="code" class="html"><span style="font-size:14px;"> if(buffer!=null) {
bundle.putString("newcontent",buffer);
ms.setData(bundle);
myHandler.sendMessage(ms);</span>

这样就会导致UI无法更新,也就是无法看到Server的返回结果.因为readline()一直不会返回null,除非将Server的原代码 

while ((line = in.readLine()) != null) {
buffer = buffer + line;
//System.out.println(buffer);
output.println(packetMessage(buffer));
output.flush();
}

改为

while ((line = in.readLine()) != null) {
buffer = buffer + line;
//System.out.println(buffer);
output.println(packetMessage(buffer));
output.flush();
socket.shutdownOutput();
}
也就是在每个循环末尾执行socket.shutdownOutput().不过导致每次提交都只能读一行(readline()的功能造成的).