前面服务器和客户端只是进行了简单的通信操作:服务器接收到客户端连接之后
服务器向客户端输出一个字符串,而客户端也只是读取服务器的字符串之后就退出了
而实际 应用中的客户端则可能需要和服务器保持长时间的通信,即服务器
需要不断的读取客户端的数据,并向客户端写入数据;客户端也需要不断的
读取服务器,并向数据库中写入数据。
使用传统BufferedReader的 readLine 方法读取数据时,当该方法成功返回之前,该线程
是阻塞的,程序无法继续进行。所以,服务器应该为每一个Socket开启一个线程,每条线程
负责与一个客户端进行通信。
服务器端:
class ServerThread implements Runnable { Socket s; BufferedReader br; public ServerThread(Socket s) throws IOException { super(); this.s = s; br = new BufferedReader(new InputStreamReader(s.getInputStream())); } @Override public void run() { String content = null; try { while ((content = readFromClient()) != null) { for (Socket s : MultiThreadServer.sockets) { OutputStream os; os = s.getOutputStream(); os.write((content + "\n").getBytes("utf-8")); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private String readFromClient() { try { return br.readLine(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } } public class MultiThreadServer { public static ArrayList<Socket> sockets = new ArrayList<Socket>(); public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(45623); while (true) { Socket s = ss.accept(); sockets.add(s); new Thread(new ServerThread(s)).start(); } } }
每当客户端Socket连接到该ServerSocket之后,
程序将对应Socket加入到集合中保存,并为该Socket开启一条线程,负责处理该Socket的
所有通信事务。
客户端:
MultiClient:
public class MultiThreadClient extends Activity { EditText input; TextView show; Button send; Handler handler; ClientThread clientThread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_client_thread); input = (EditText) findViewById(R.id.input); show = (TextView) findViewById(R.id.show); send = (Button) findViewById(R.id.send); handler = new Handler() { @Override public void handleMessage(Message msg) { // 如果消息来自于子线程 if (msg.what == 0x123) { // 将读取的内容追加显示在文本中 show.append("\n" + msg.obj.toString()); } } }; clientThread = new ClientThread(handler); // 客户端启动ClientThread线程创建网络连接、读取来自服务器的数据 new Thread(clientThread).start(); send.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 当用户按下发送按钮后,将用户输入的数据封装成Message,然后发送给子线程的handler Message msg = new Message(); msg.what = 0x345; msg.obj = input.getText().toString(); clientThread.revHandler.sendMessage(msg); input.setText(""); } }); } }
ClientThread:
public class ClientThread implements Runnable { private Socket s; // 定义向UI线程发送消息的Handler对象。 private Handler handler; // 定义接收UI线程的消息的Handler对象. public Handler revHandler; // 该线程所处理的Socket所对用的流对象 BufferedReader br; OutputStream os; public ClientThread(Handler handler) { super(); this.handler = handler; } @Override public void run() { try { s = new Socket("192.168.3.12", 45623); br = new BufferedReader(new InputStreamReader(s.getInputStream())); os = s.getOutputStream(); // 启动一条子线程 new Thread() { public void run() { String content = null; // 不断读取Socket输入流中的内容 try { while ((content = br.readLine()) != null) { // 每当读到来自服务器的数据之后,发送消息通知程序界面显示该数据 Message msg = new Message(); msg.what = 0x123; msg.obj = content; handler.sendMessage(msg); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }; }.start(); // 为当前线程初始化Looper Looper.prepare(); // 创建revhandler对象 revHandler = new Handler() { @Override public void handleMessage(Message msg) { // 接收UI线程中用户输入的数据 if (msg.what == 0x345) { // 将用户在文本框内输入的内容写入网络 try { os.write((msg.obj.toString() + "\r\n").getBytes("utf-8")); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; Looper.loop(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
时间: 2024-11-04 18:06:35