Android 群聊天室

来源:互联网 发布:常州科教城淘宝装修 编辑:程序博客网 时间:2024/05/29 09:26
Top

THREAD_SORCKET DAY05

  1. 解析XML生成Point对象
  2. 实现群聊天室

1 解析XML生成Point对象

1.1 问题

要求解析如下xml文件,并且生成Point对象。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans>
  3. <bean name="point" class="day23.Point">
  4. <param name="x" value="10"/>
  5. <param name="y" value="20"/>
  6. </bean>
  7. </beans>

1.2 方案

利用dom4j解析xml首先要把dom4j-1.6.1.jar和jaxen-1.1-beta-6.jar这2个jar包拷贝到项目的lib目录下,并配置build path。把xml文件也拷贝到项目中,根据xml的格式创建相对应的Point实体类,然后编写BeanFactory类去解析xml文件。

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写Point实体类

创建一个java项目,创建包,包中创建一个类Point,根据xml的格式编写类的属性,并复写toString方法。代码如下所示:

  1. package tarena.gsd;
  2. //所有的类都一个类对象,其类型为Class类型
  3. public class Point {//Point.class
  4.     private int x;//也是对象,类型为Field类型
  5.     private int y;
  6.     
  7.     public double distance(Point point){
  8.      int a=x-point.x;
  9.      int b=y-point.y;
  10.      return Math.sqrt(a*a+b*b);
  11.     }
  12.     @Override
  13.     public String toString() {
  14.         return "Point [x=" + x + ", y=" + y + "]";
  15.     }
  16.     
  17. }

步骤二:编写BeanFactory类

编写BeanFactory类,用于解析xml文件,在类中定义2个静态的属性,类型是Map,用于存放解析元素的key和value值。代码如下:

  1. package tarena.gsd;
  2. import java.io.FileNotFoundException;
  3. import java.lang.reflect.Field;
  4. import java.util.HashMap;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Set;
  10. import org.dom4j.Document;
  11. import org.dom4j.DocumentException;
  12. import org.dom4j.Element;
  13. import org.dom4j.Node;
  14. import org.dom4j.io.SAXReader;
  15. public class BeanFactory {// 工厂
  16.     /**
  17.      * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
  18.      * value:class属性对应的值
  19.      */
  20.     private static Map<String, String> beanMap = new HashMap<String, String>();
  21.     // key:bean的name属性值
  22.     // value:bean中的参数
  23.     private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
  24.     // 当类中有多个静态属性(类变量)或静态代码块时
  25.     
  26. }

步骤三:编写newInstance方法

在BeanFactory类中编写一个newInstance方法,返回值类型是Object,参数是1个String型变量,方法中根据传入的值,用反射创建对象,并用map的值构建对象属性,最后把对象返回。代码如下:

  1. package tarena.gsd;
  2. import java.io.FileNotFoundException;
  3. import java.lang.reflect.Field;
  4. import java.util.HashMap;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Set;
  10. import org.dom4j.Document;
  11. import org.dom4j.DocumentException;
  12. import org.dom4j.Element;
  13. import org.dom4j.Node;
  14. import org.dom4j.io.SAXReader;
  15. public class BeanFactory {// 工厂
  16.     /**
  17.      * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
  18.      * value:class属性对应的值
  19.      */
  20.     private static Map<String, String> beanMap = new HashMap<String, String>();
  21.     // key:bean的name属性值
  22.     // value:bean中的参数
  23.     private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
  24.     
  25.     public static Object newInstance(String key) throws Exception {
  26.         // 通过反射构建类的对象
  27.         Class<?> c = Class.forName(beanMap.get(key));// 类对象
  28.         Object obj = c.newInstance();// 构建类的对象
  29.         // 通过反射给类的属性赋值
  30.         Map<String, String> params = paramMap.get(key);
  31.         // 遍历Map(首先应将map转换为set)
  32.         Set<Entry<String, String>> es = params.entrySet();
  33.         // 遍历set
  34.         for (Entry<String, String> e : es) {
  35.             // 通过类对象获得属性对象
  36.             Field f = c.getDeclaredField(e.getKey());
  37.             // 设置属性对象可访问
  38.             f.setAccessible(true);// 设置属性可访问
  39.             // 给obj对象的f属性赋值
  40.             f.set(obj, Integer.valueOf(e.getValue()));
  41.         }
  42.         return obj;
  43.     }
  44. }

步骤四:编写parse方法

在BeanFactory类中编写一个parse方法,返回值类型是Document,参数是1个String型变量,方法中根据传入的值,把传入的文件转换为Document对象并返回。代码如下:

  1. package tarena.gsd;
  2. import java.io.FileNotFoundException;
  3. import java.lang.reflect.Field;
  4. import java.util.HashMap;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Set;
  10. import org.dom4j.Document;
  11. import org.dom4j.DocumentException;
  12. import org.dom4j.Element;
  13. import org.dom4j.Node;
  14. import org.dom4j.io.SAXReader;
  15. public class BeanFactory {// 工厂
  16.     /**
  17.      * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
  18.      * value:class属性对应的值
  19.      */
  20.     private static Map<String, String> beanMap = new HashMap<String, String>();
  21.     // key:bean的name属性值
  22.     // value:bean中的参数
  23.     private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
  24.     // alt+shift+m (快速提取代码块为方法)
  25.     private static Document parse(String fileName) throws DocumentException,
  26.             FileNotFoundException {
  27.         // 1.构建解析器对象
  28.         SAXReader r = new SAXReader();
  29.         // 2.解析文件
  30.         // return r.read(
  31.         // new FileInputStream(fileName));
  32.         return r.read(BeanFactory.class.// 获得类对象
  33.                 getClassLoader(). // 类加载器对象(负责将类读到内存的对象)
  34.                 getResourceAsStream(fileName));// 从类路径读取
  35.     }
  36.     public static Object newInstance(String key) throws Exception {
  37.         // 通过反射构建类的对象
  38.         Class<?> c = Class.forName(beanMap.get(key));// 类对象
  39.         Object obj = c.newInstance();// 构建类的对象
  40.         // 通过反射给类的属性赋值
  41.         Map<String, String> params = paramMap.get(key);
  42.         // 遍历Map(首先应将map转换为set)
  43.         Set<Entry<String, String>> es = params.entrySet();
  44.         // 遍历set
  45.         for (Entry<String, String> e : es) {
  46.             // 通过类对象获得属性对象
  47.             Field f = c.getDeclaredField(e.getKey());
  48.             // 设置属性对象可访问
  49.             f.setAccessible(true);// 设置属性可访问
  50.             // 给obj对象的f属性赋值
  51.             f.set(obj, Integer.valueOf(e.getValue()));
  52.         }
  53.         return obj;
  54.     }
  55. }

步骤五:编写processParams方法

在BeanFactory类中编写一个processParams方法。该方法无返回值类型,参数是1个Node类型变量,1个是 String型变量,方法中把传入的Node节点解析出来,根据String值放入对象的map中。代码如下:

  1. package tarena.gsd;
  2. import java.io.FileNotFoundException;
  3. import java.lang.reflect.Field;
  4. import java.util.HashMap;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Set;
  10. import org.dom4j.Document;
  11. import org.dom4j.DocumentException;
  12. import org.dom4j.Element;
  13. import org.dom4j.Node;
  14. import org.dom4j.io.SAXReader;
  15. public class BeanFactory {// 工厂
  16.     /**
  17.      * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
  18.      * value:class属性对应的值
  19.      */
  20.     private static Map<String, String> beanMap = new HashMap<String, String>();
  21.     // key:bean的name属性值
  22.     // value:bean中的参数
  23.     private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
  24.     private static void processParams(Node n, String key) {
  25.         Element e = (Element) n;
  26.         @SuppressWarnings("unchecked")
  27.         Iterator<Element> it = e.elementIterator("param");
  28.         Map<String, String> map1 = new HashMap<String, String>();
  29.         while (it.hasNext()) {
  30.             Element e1 = it.next();
  31.             String k = e1.attributeValue("name");
  32.             String v = e1.attributeValue("value");
  33.             map1.put(k, v);
  34.         }
  35.         paramMap.put(key, map1);
  36.     }
  37.     // alt+shift+m (快速提取代码块为方法)
  38.     private static Document parse(String fileName) throws DocumentException,
  39.             FileNotFoundException {
  40.         // 1.构建解析器对象
  41.         SAXReader r = new SAXReader();
  42.         // 2.解析文件
  43.         // return r.read(
  44.         // new FileInputStream(fileName));
  45.         return r.read(BeanFactory.class.// 获得类对象
  46.                 getClassLoader(). // 类加载器对象(负责将类读到内存的对象)
  47.                 getResourceAsStream(fileName));// 从类路径读取
  48.     }
  49.     public static Object newInstance(String key) throws Exception {
  50.         // 通过反射构建类的对象
  51.         Class<?> c = Class.forName(beanMap.get(key));// 类对象
  52.         Object obj = c.newInstance();// 构建类的对象
  53.         // 通过反射给类的属性赋值
  54.         Map<String, String> params = paramMap.get(key);
  55.         // 遍历Map(首先应将map转换为set)
  56.         Set<Entry<String, String>> es = params.entrySet();
  57.         // 遍历set
  58.         for (Entry<String, String> e : es) {
  59.             // 通过类对象获得属性对象
  60.             Field f = c.getDeclaredField(e.getKey());
  61.             // 设置属性对象可访问
  62.             f.setAccessible(true);// 设置属性可访问
  63.             // 给obj对象的f属性赋值
  64.             f.set(obj, Integer.valueOf(e.getValue()));
  65.         }
  66.         return obj;
  67.     }
  68. }

步骤六:编写processData方法

在BeanFactory类中编写一个processData方法,方法无返回值类型,参数是1个Document类型变量,在方法中把传入的Document对象解析出来,放入相应的map中。代码如下:

  1. package tarena.gsd;
  2. import java.io.FileNotFoundException;
  3. import java.lang.reflect.Field;
  4. import java.util.HashMap;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Set;
  10. import org.dom4j.Document;
  11. import org.dom4j.DocumentException;
  12. import org.dom4j.Element;
  13. import org.dom4j.Node;
  14. import org.dom4j.io.SAXReader;
  15. public class BeanFactory {// 工厂
  16.     /**
  17.      * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
  18.      * value:class属性对应的值
  19.      */
  20.     private static Map<String, String> beanMap = new HashMap<String, String>();
  21.     // key:bean的name属性值
  22.     // value:bean中的参数
  23.     private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
  24.     // 当类中有多个静态属性(类变量)或静态代码块时
  25.     // 其执行顺序是从上到下
  26.     private static void processData(Document doc) {
  27.         // 3.处理数据
  28.         // 3.1获得所有的Bean节点
  29.         @SuppressWarnings("unchecked")
  30.         List<Node> nodes = doc.selectNodes("/beans/bean");// xpath
  31.         // 3.2遍历Bean节点,取出每个节点的内容封装到Map
  32.         for (Node n : nodes) {
  33.             String key = n.valueOf("@name");
  34.             String value = n.valueOf("@class");
  35.             beanMap.put(key, value);
  36.             processParams(n, key);
  37.         }
  38.     }
  39.     private static void processParams(Node n, String key) {
  40.         Element e = (Element) n;
  41.         @SuppressWarnings("unchecked")
  42.         Iterator<Element> it = e.elementIterator("param");
  43.         Map<String, String> map1 = new HashMap<String, String>();
  44.         while (it.hasNext()) {
  45.             Element e1 = it.next();
  46.             String k = e1.attributeValue("name");
  47.             String v = e1.attributeValue("value");
  48.             map1.put(k, v);
  49.         }
  50.         paramMap.put(key, map1);
  51.     }
  52.     // alt+shift+m (快速提取代码块为方法)
  53.     private static Document parse(String fileName) throws DocumentException,
  54.             FileNotFoundException {
  55.         // 1.构建解析器对象
  56.         SAXReader r = new SAXReader();
  57.         // 2.解析文件
  58.         // return r.read(
  59.         // new FileInputStream(fileName));
  60.         return r.read(BeanFactory.class.// 获得类对象
  61.                 getClassLoader(). // 类加载器对象(负责将类读到内存的对象)
  62.                 getResourceAsStream(fileName));// 从类路径读取
  63.     }
  64.     public static Object newInstance(String key) throws Exception {
  65.         // 通过反射构建类的对象
  66.         Class<?> c = Class.forName(beanMap.get(key));// 类对象
  67.         Object obj = c.newInstance();// 构建类的对象
  68.         // 通过反射给类的属性赋值
  69.         Map<String, String> params = paramMap.get(key);
  70.         // 遍历Map(首先应将map转换为set)
  71.         Set<Entry<String, String>> es = params.entrySet();
  72.         // 遍历set
  73.         for (Entry<String, String> e : es) {
  74.             // 通过类对象获得属性对象
  75.             Field f = c.getDeclaredField(e.getKey());
  76.             // 设置属性对象可访问
  77.             f.setAccessible(true);// 设置属性可访问
  78.             // 给obj对象的f属性赋值
  79.             f.set(obj, Integer.valueOf(e.getValue()));
  80.         }
  81.         return obj;
  82.     }
  83.     public static void main(String[] args) throws Exception {
  84.         // System.out.println(map);
  85.         // System.out.println(paramMap);
  86.         Point p = (Point) BeanFactory.newInstance("point");
  87.         System.out.println(p);
  88.     }
  89. }

步骤七:编写loadBeans方法

在BeanFactory类中编写一个loadBeans方法,方法无返回值类型,参数是1个String类型变量,在方法中调用parse方法把传入的String变量传入方法,获取Document对象,再调用processData把Document对象放入。代码如下:

  1. package tarena.gsd;
  2. import java.io.FileNotFoundException;
  3. import java.lang.reflect.Field;
  4. import java.util.HashMap;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Set;
  10. import org.dom4j.Document;
  11. import org.dom4j.DocumentException;
  12. import org.dom4j.Element;
  13. import org.dom4j.Node;
  14. import org.dom4j.io.SAXReader;
  15. public class BeanFactory {// 工厂
  16.     /**
  17.      * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
  18.      * value:class属性对应的值
  19.      */
  20.     private static Map<String, String> beanMap = new HashMap<String, String>();
  21.     // key:bean的name属性值
  22.     // value:bean中的参数
  23.     private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
  24.     // 此方法将读取的内容封装到map
  25.     private static void loadBeans(String fileName) throws Exception {
  26.         // 解析文件
  27.         Document doc = parse(fileName);
  28.         // 处理数据
  29.         processData(doc);
  30.     }
  31.     private static void processData(Document doc) {
  32.         // 3.处理数据
  33.         // 3.1获得所有的Bean节点
  34.         @SuppressWarnings("unchecked")
  35.         List<Node> nodes = doc.selectNodes("/beans/bean");// xpath
  36.         // 3.2遍历Bean节点,取出每个节点的内容封装到Map
  37.         for (Node n : nodes) {
  38.             String key = n.valueOf("@name");
  39.             String value = n.valueOf("@class");
  40.             beanMap.put(key, value);
  41.             processParams(n, key);
  42.         }
  43.     }
  44.     private static void processParams(Node n, String key) {
  45.         Element e = (Element) n;
  46.         @SuppressWarnings("unchecked")
  47.         Iterator<Element> it = e.elementIterator("param");
  48.         Map<String, String> map1 = new HashMap<String, String>();
  49.         while (it.hasNext()) {
  50.             Element e1 = it.next();
  51.             String k = e1.attributeValue("name");
  52.             String v = e1.attributeValue("value");
  53.             map1.put(k, v);
  54.         }
  55.         paramMap.put(key, map1);
  56.     }
  57.     // alt+shift+m (快速提取代码块为方法)
  58.     private static Document parse(String fileName) throws DocumentException,
  59.             FileNotFoundException {
  60.         // 1.构建解析器对象
  61.         SAXReader r = new SAXReader();
  62.         // 2.解析文件
  63.         // return r.read(
  64.         // new FileInputStream(fileName));
  65.         return r.read(BeanFactory.class.// 获得类对象
  66.                 getClassLoader(). // 类加载器对象(负责将类读到内存的对象)
  67.                 getResourceAsStream(fileName));// 从类路径读取
  68.     }
  69.     public static Object newInstance(String key) throws Exception {
  70.         // 通过反射构建类的对象
  71.         Class<?> c = Class.forName(beanMap.get(key));// 类对象
  72.         Object obj = c.newInstance();// 构建类的对象
  73.         // 通过反射给类的属性赋值
  74.         Map<String, String> params = paramMap.get(key);
  75.         // 遍历Map(首先应将map转换为set)
  76.         Set<Entry<String, String>> es = params.entrySet();
  77.         // 遍历set
  78.         for (Entry<String, String> e : es) {
  79.             // 通过类对象获得属性对象
  80.             Field f = c.getDeclaredField(e.getKey());
  81.             // 设置属性对象可访问
  82.             f.setAccessible(true);// 设置属性可访问
  83.             // 给obj对象的f属性赋值
  84.             f.set(obj, Integer.valueOf(e.getValue()));
  85.         }
  86.         return obj;
  87.     }
  88.     public static void main(String[] args) throws Exception {
  89.         // System.out.println(map);
  90.         // System.out.println(paramMap);
  91.         Point p = (Point) BeanFactory.newInstance("point");
  92.         System.out.println(p);
  93.     }
  94. }

步骤八:静态加载

在BeanFactory类中编写一个静态代码块,代码块中调用loadBeans方法,去加载xml文件。代码如下:

  1. package tarena.gsd;
  2. import java.io.FileNotFoundException;
  3. import java.lang.reflect.Field;
  4. import java.util.HashMap;
  5. import java.util.Iterator;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9. import java.util.Set;
  10. import org.dom4j.Document;
  11. import org.dom4j.DocumentException;
  12. import org.dom4j.Element;
  13. import org.dom4j.Node;
  14. import org.dom4j.io.SAXReader;
  15. public class BeanFactory {// 工厂
  16.     /**
  17.      * 借助此类读取beans.xml文件(类加载时读取) 并将文件中的内容封装到Map集合中 要求: key:为name属性对应的值
  18.      * value:class属性对应的值
  19.      */
  20.     private static Map<String, String> beanMap = new HashMap<String, String>();
  21.     // key:bean的name属性值
  22.     // value:bean中的参数
  23.     private static Map<String, Map<String, String>> paramMap = new HashMap<String, Map<String, String>>();
  24.     // 当类中有多个静态属性(类变量)或静态代码块时
  25.     // 其执行顺序是从上到下
  26.     static {
  27.         try {
  28.             loadBeans("beans.xml");
  29.         } catch (Exception e) {
  30.             e.printStackTrace();
  31.         }
  32.     }
  33.     // 此方法将读取的内容封装到map
  34.     private static void loadBeans(String fileName) throws Exception {
  35.         // 解析文件
  36.         Document doc = parse(fileName);
  37.         // 处理数据
  38.         processData(doc);
  39.     }
  40.     private static void processData(Document doc) {
  41.         // 3.处理数据
  42.         // 3.1获得所有的Bean节点
  43.         @SuppressWarnings("unchecked")
  44.         List<Node> nodes = doc.selectNodes("/beans/bean");// xpath
  45.         // 3.2遍历Bean节点,取出每个节点的内容封装到Map
  46.         for (Node n : nodes) {
  47.             String key = n.valueOf("@name");
  48.             String value = n.valueOf("@class");
  49.             beanMap.put(key, value);
  50.             processParams(n, key);
  51.         }
  52.     }
  53.     private static void processParams(Node n, String key) {
  54.         Element e = (Element) n;
  55.         @SuppressWarnings("unchecked")
  56.         Iterator<Element> it = e.elementIterator("param");
  57.         Map<String, String> map1 = new HashMap<String, String>();
  58.         while (it.hasNext()) {
  59.             Element e1 = it.next();
  60.             String k = e1.attributeValue("name");
  61.             String v = e1.attributeValue("value");
  62.             map1.put(k, v);
  63.         }
  64.         paramMap.put(key, map1);
  65.     }
  66.     // alt+shift+m (快速提取代码块为方法)
  67.     private static Document parse(String fileName) throws DocumentException,
  68.             FileNotFoundException {
  69.         // 1.构建解析器对象
  70.         SAXReader r = new SAXReader();
  71.         // 2.解析文件
  72.         // return r.read(
  73.         // new FileInputStream(fileName));
  74.         return r.read(BeanFactory.class.// 获得类对象
  75.                 getClassLoader(). // 类加载器对象(负责将类读到内存的对象)
  76.                 getResourceAsStream(fileName));// 从类路径读取
  77.     }
  78.     public static Object newInstance(String key) throws Exception {
  79.         // 通过反射构建类的对象
  80.         Class<?> c = Class.forName(beanMap.get(key));// 类对象
  81.         Object obj = c.newInstance();// 构建类的对象
  82.         // 通过反射给类的属性赋值
  83.         Map<String, String> params = paramMap.get(key);
  84.         // 遍历Map(首先应将map转换为set)
  85.         Set<Entry<String, String>> es = params.entrySet();
  86.         // 遍历set
  87.         for (Entry<String, String> e : es) {
  88.             // 通过类对象获得属性对象
  89.             Field f = c.getDeclaredField(e.getKey());
  90.             // 设置属性对象可访问
  91.             f.setAccessible(true);// 设置属性可访问
  92.             // 给obj对象的f属性赋值
  93.             f.set(obj, Integer.valueOf(e.getValue()));
  94.         }
  95.         return obj;
  96.     }
  97.     public static void main(String[] args) throws Exception {
  98.         // System.out.println(map);
  99.         // System.out.println(paramMap);
  100.         Point p = (Point) BeanFactory.newInstance("point");
  101.         System.out.println(p);
  102.     }
  103. }

1.4 完整代码

本案例Point类的完整代码如下所示:

代码

本案例客户端的完整代码如下所示:

代码

2 实现群聊天室

2.1 问题

要求编写一个Android程序,界面如图-1所示:

图-1

点击连接按钮后可以建立与服务器连接,连接后点击发送可以把发送的文本显示在所有连接客户端的ListView中,实现类似群聊的功能。

2.2 方案

先编写服务器程序,使用Vector 对象来存储所有连接上的客户端,使用ServerSocket来监听客户端连接,连接成功后存放入Vector中,在创建一个线程用于读取客户发送消息,并遍历Vector,把消息发送给所有客户端。

再编写Android客户端,按照界面要求编写界面,再编写Activity,在点击连接按钮后开启新线程,再使用Socket与服务端建立连接。在点击发送按钮后开启新线程,把输入框内容发送给服务端。在主程序中再开启一个新线程用于接收服务端发送过来的消息,并显示在ListView中。

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:编写布局文件

创建Android项目,相对布局编写布局文件,布局文件中放入2个EditText,2个按钮和1个ListView。代码如下所示:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. tools:context=".MainActivity" >
  6. <LinearLayout
  7. android:id="@+id/linearLayout1"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. android:layout_alignParentLeft="true"
  11. android:layout_alignParentRight="true"
  12. android:layout_alignParentTop="true" >
  13. <EditText
  14. android:id="@+id/nickname"
  15. android:layout_width="0dp"
  16. android:layout_height="wrap_content"
  17. android:layout_weight="1"
  18. android:ems="10"
  19. android:enabled="false">
  20. <requestFocus />
  21. </EditText>
  22. <Button
  23. android:id="@+id/connect"
  24. android:layout_width="wrap_content"
  25. android:layout_height="wrap_content"
  26. android:onClick="doClick"
  27. android:text="建立连接" />
  28. </LinearLayout>
  29. <ListView
  30. android:id="@+id/listView"
  31. android:layout_width="match_parent"
  32. android:layout_height="wrap_content"
  33. android:layout_above="@+id/LinearLayout01"
  34. android:layout_alignParentLeft="true"
  35. android:layout_below="@+id/linearLayout1" >
  36. </ListView>
  37. <LinearLayout
  38. android:id="@+id/LinearLayout01"
  39. android:layout_width="wrap_content"
  40. android:layout_height="wrap_content"
  41. android:layout_alignParentBottom="true"
  42. android:layout_alignParentLeft="true"
  43. android:layout_alignParentRight="true" >
  44. <EditText
  45. android:id="@+id/text"
  46. android:layout_width="0dp"
  47. android:layout_height="wrap_content"
  48. android:layout_weight="1"
  49. android:ems="10"
  50. android:hint="请输入发送文本" />
  51. <Button
  52. android:id="@+id/send"
  53. android:layout_width="wrap_content"
  54. android:onClick="doClick"
  55. android:layout_height="wrap_content"
  56. android:text="发送" />
  57. </LinearLayout>
  58. </RelativeLayout>

步骤二:编写MainActivity类

编写MainActivity类,类中编写一个init方法,方法中获取界面的所有控件,并赋值给成员变量。在onCreate方法中调用init。在定义一个Handler类型的成员变量,实例化这个变量,并复写handleMessage方法,在方法中使用switch语句处理不同的消息。代码如下所示:

  1. package com.tarena.thread_socket_day05_cookbook_02_client;
  2. import java.io.DataInputStream;
  3. import java.io.DataOutputStream;
  4. import java.io.IOException;
  5. import java.net.InetSocketAddress;
  6. import java.net.Socket;
  7. import java.net.SocketAddress;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import android.os.Bundle;
  11. import android.os.Handler;
  12. import android.app.Activity;
  13. import android.view.Menu;
  14. import android.view.View;
  15. import android.widget.ArrayAdapter;
  16. import android.widget.Button;
  17. import android.widget.EditText;
  18. import android.widget.ListView;
  19. import android.widget.Toast;
  20. public class MainActivity extends Activity {
  21.     private EditText etNickname;
  22.     private EditText etText;
  23.     private Button connect;
  24.     private boolean isConnected;
  25.     private ListView lv;
  26.     private List<String> messages=new ArrayList<String>();
  27.     private ArrayAdapter<String> adapter;
  28.     private Socket socket;
  29.     private DataInputStream is;
  30.     private DataOutputStream os;
  31.     
  32.     private Handler handler=new Handler(){
  33.         public void handleMessage(android.os.Message msg) {
  34.             switch (msg.what) {
  35.             case HANDLER_CONNECT_SUCCESS:
  36.                 connect.setText("连接已建立");
  37.                 etNickname.setText(socket.getInetAddress().toString());
  38.                 etNickname.setEnabled(false);
  39.                 connect.setEnabled(false);
  40.                 isConnected = true;
  41.                 //把messages中的数据呈现在listView中
  42.                 adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, messages);
  43.                 lv.setAdapter(adapter);
  44.                 // 启动读取服务端返回聊天记录的线程
  45.                 new Reader().start();
  46.                 break;
  47.                 
  48.             case HANDLER_RECEIVE_MESSAGE:
  49.                 adapter.notifyDataSetChanged();
  50.                 break;
  51.             
  52.             case HANDLER_CONNECT_TIMEOUT:
  53.                 Toast.makeText(MainActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
  54.                 break;
  55.             }
  56.         }
  57.     };
  58.     
  59.     public static final int HANDLER_CONNECT_SUCCESS = 0;
  60.     public static final int HANDLER_RECEIVE_MESSAGE = 1;
  61.     public static final int HANDLER_CONNECT_TIMEOUT = 2;
  62.     
  63.     
  64.     @Override
  65.     protected void onCreate(Bundle savedInstanceState) {
  66.         super.onCreate(savedInstanceState);
  67.         setContentView(R.layout.activity_main);
  68.         init();
  69.     }
  70.     private void init() {
  71.         etNickname = (EditText) findViewById(R.id.nickname);
  72.         etText = (EditText) findViewById(R.id.text);
  73.         connect = (Button) findViewById(R.id.connect);
  74.         lv = (ListView) findViewById(R.id.listView);
  75.     }
  76.     
  77. }

步骤三:编写客户端读取线程类

编写一个内部类Reader继承自Thread,复写run方法,在方法中使用无限循环,不断读取服务器发来的信息,接收后发消息通知界面去更新。代码如下所示:

  1. package com.tarena.thread_socket_day05_cookbook_02_client;
  2. import java.io.DataInputStream;
  3. import java.io.DataOutputStream;
  4. import java.io.IOException;
  5. import java.net.InetSocketAddress;
  6. import java.net.Socket;
  7. import java.net.SocketAddress;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import android.os.Bundle;
  11. import android.os.Handler;
  12. import android.app.Activity;
  13. import android.view.Menu;
  14. import android.view.View;
  15. import android.widget.ArrayAdapter;
  16. import android.widget.Button;
  17. import android.widget.EditText;
  18. import android.widget.ListView;
  19. import android.widget.Toast;
  20. public class MainActivity extends Activity {
  21.     private EditText etNickname;
  22.     private EditText etText;
  23.     private Button connect;
  24.     private boolean isConnected;
  25.     private ListView lv;
  26.     private List<String> messages=new ArrayList<String>();
  27.     private ArrayAdapter<String> adapter;
  28.     private Socket socket;
  29.     private DataInputStream is;
  30.     private DataOutputStream os;
  31.     
  32.     private Handler handler=new Handler(){
  33.         public void handleMessage(android.os.Message msg) {
  34.             switch (msg.what) {
  35.             case HANDLER_CONNECT_SUCCESS:
  36.                 connect.setText("连接已建立");
  37.                 etNickname.setText(socket.getInetAddress().toString());
  38.                 etNickname.setEnabled(false);
  39.                 connect.setEnabled(false);
  40.                 isConnected = true;
  41.                 //把messages中的数据呈现在listView中
  42.                 adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, messages);
  43.                 lv.setAdapter(adapter);
  44.                 // 启动读取服务端返回聊天记录的线程
  45.                 new Reader().start();
  46.                 break;
  47.                 
  48.             case HANDLER_RECEIVE_MESSAGE:
  49.                 adapter.notifyDataSetChanged();
  50.                 break;
  51.             
  52.             case HANDLER_CONNECT_TIMEOUT:
  53.                 Toast.makeText(MainActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
  54.                 break;
  55.             }
  56.         }
  57.     };
  58.     
  59.     public static final int HANDLER_CONNECT_SUCCESS = 0;
  60.     public static final int HANDLER_RECEIVE_MESSAGE = 1;
  61.     public static final int HANDLER_CONNECT_TIMEOUT = 2;
  62.     
  63.     
  64.     @Override
  65.     protected void onCreate(Bundle savedInstanceState) {
  66.         super.onCreate(savedInstanceState);
  67.         setContentView(R.layout.activity_main);
  68.         init();
  69.     }
  70.     private void init() {
  71.         etNickname = (EditText) findViewById(R.id.nickname);
  72.         etText = (EditText) findViewById(R.id.text);
  73.         connect = (Button) findViewById(R.id.connect);
  74.         lv = (ListView) findViewById(R.id.listView);
  75.     }
  76.     class Reader extends Thread {
  77.         public Reader() {
  78.         }
  79.         // 接收服务端输入进客户端的信息 并且显示在ListView中
  80.         public void run() {
  81.             try {
  82.                 while (isConnected) {
  83.                     String data = is.readUTF();
  84.                     //更新listView的数据源
  85.                     messages.add(data);
  86.                     //发消息给Handler 更新listview
  87.                     handler.sendEmptyMessage(HANDLER_RECEIVE_MESSAGE);
  88.                 }
  89.                 is.close();
  90.             } catch (IOException e) {
  91.                 e.printStackTrace();
  92.             }
  93.         }
  94.     }
  95. }

步骤四:编写send方法

编写MainActivity中编写send方法,在方法中获取输入框内容,开启新线程把内容发送给服务端。代码如下所示:

  1. package com.tarena.thread_socket_day05_cookbook_02_client;
  2. import java.io.DataInputStream;
  3. import java.io.DataOutputStream;
  4. import java.io.IOException;
  5. import java.net.InetSocketAddress;
  6. import java.net.Socket;
  7. import java.net.SocketAddress;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import android.os.Bundle;
  11. import android.os.Handler;
  12. import android.app.Activity;
  13. import android.view.Menu;
  14. import android.view.View;
  15. import android.widget.ArrayAdapter;
  16. import android.widget.Button;
  17. import android.widget.EditText;
  18. import android.widget.ListView;
  19. import android.widget.Toast;
  20. public class MainActivity extends Activity {
  21.     private EditText etNickname;
  22.     private EditText etText;
  23.     private Button connect;
  24.     private boolean isConnected;
  25.     private ListView lv;
  26.     private List<String> messages=new ArrayList<String>();
  27.     private ArrayAdapter<String> adapter;
  28.     private Socket socket;
  29.     private DataInputStream is;
  30.     private DataOutputStream os;
  31.     
  32.     private Handler handler=new Handler(){
  33.         public void handleMessage(android.os.Message msg) {
  34.             switch (msg.what) {
  35.             case HANDLER_CONNECT_SUCCESS:
  36.                 connect.setText("连接已建立");
  37.                 etNickname.setText(socket.getInetAddress().toString());
  38.                 etNickname.setEnabled(false);
  39.                 connect.setEnabled(false);
  40.                 isConnected = true;
  41.                 //把messages中的数据呈现在listView中
  42.                 adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, messages);
  43.                 lv.setAdapter(adapter);
  44.                 // 启动读取服务端返回聊天记录的线程
  45.                 new Reader().start();
  46.                 break;
  47.                 
  48.             case HANDLER_RECEIVE_MESSAGE:
  49.                 adapter.notifyDataSetChanged();
  50.                 break;
  51.             
  52.             case HANDLER_CONNECT_TIMEOUT:
  53.                 Toast.makeText(MainActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
  54.                 break;
  55.             }
  56.         }
  57.     };
  58.     
  59.     public static final int HANDLER_CONNECT_SUCCESS = 0;
  60.     public static final int HANDLER_RECEIVE_MESSAGE = 1;
  61.     public static final int HANDLER_CONNECT_TIMEOUT = 2;
  62.     
  63.     
  64.     @Override
  65.     protected void onCreate(Bundle savedInstanceState) {
  66.         super.onCreate(savedInstanceState);
  67.         setContentView(R.layout.activity_main);
  68.         init();
  69.     }
  70.     private void init() {
  71.         etNickname = (EditText) findViewById(R.id.nickname);
  72.         etText = (EditText) findViewById(R.id.text);
  73.         connect = (Button) findViewById(R.id.connect);
  74.         lv = (ListView) findViewById(R.id.listView);
  75.     }
  76.     //发送消息
  77.     private void send() {
  78.         if(!isConnected){
  79.             Toast.makeText(this, "请先建立连接", Toast.LENGTH_SHORT).show();
  80.             return;
  81.         }
  82.         //发送消息
  83.         //获取输出流输出 网络访问需要在工作线程中完成
  84.         new Thread(){
  85.             public void run() {
  86.                 try {
  87.                     String text=etText.getText().toString();
  88.                     os.writeUTF(text);
  89.                     os.flush();
  90.                 } catch (IOException e) {
  91.                     e.printStackTrace();
  92.                 }
  93.             }
  94.         }.start();
  95.     }
  96.     class Reader extends Thread {
  97.         public Reader() {
  98.         }
  99.         // 接收服务端输入进客户端的信息 并且显示在ListView中
  100.         public void run() {
  101.             try {
  102.                 while (isConnected) {
  103.                     String data = is.readUTF();
  104.                     //更新listView的数据源
  105.                     messages.add(data);
  106.                     //发消息给Handler 更新listview
  107.                     handler.sendEmptyMessage(HANDLER_RECEIVE_MESSAGE);
  108.                 }
  109.                 is.close();
  110.             } catch (IOException e) {
  111.                 e.printStackTrace();
  112.             }
  113.         }
  114.     }
  115. }

步骤五:编写connect方法

编写MainActivity中编写connect方法,在方法中开启新线程,使用Socket对象建立与服务端连接。代码如下所示:

  1. package com.tarena.thread_socket_day05_cookbook_02_client;
  2. import java.io.DataInputStream;
  3. import java.io.DataOutputStream;
  4. import java.io.IOException;
  5. import java.net.InetSocketAddress;
  6. import java.net.Socket;
  7. import java.net.SocketAddress;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import android.os.Bundle;
  11. import android.os.Handler;
  12. import android.app.Activity;
  13. import android.view.Menu;
  14. import android.view.View;
  15. import android.widget.ArrayAdapter;
  16. import android.widget.Button;
  17. import android.widget.EditText;
  18. import android.widget.ListView;
  19. import android.widget.Toast;
  20. public class MainActivity extends Activity {
  21.     private EditText etNickname;
  22.     private EditText etText;
  23.     private Button connect;
  24.     private boolean isConnected;
  25.     private ListView lv;
  26.     private List<String> messages=new ArrayList<String>();
  27.     private ArrayAdapter<String> adapter;
  28.     private Socket socket;
  29.     private DataInputStream is;
  30.     private DataOutputStream os;
  31.     
  32.     private Handler handler=new Handler(){
  33.         public void handleMessage(android.os.Message msg) {
  34.             switch (msg.what) {
  35.             case HANDLER_CONNECT_SUCCESS:
  36.                 connect.setText("连接已建立");
  37.                 etNickname.setText(socket.getInetAddress().toString());
  38.                 etNickname.setEnabled(false);
  39.                 connect.setEnabled(false);
  40.                 isConnected = true;
  41.                 //把messages中的数据呈现在listView中
  42.                 adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, messages);
  43.                 lv.setAdapter(adapter);
  44.                 // 启动读取服务端返回聊天记录的线程
  45.                 new Reader().start();
  46.                 break;
  47.                 
  48.             case HANDLER_RECEIVE_MESSAGE:
  49.                 adapter.notifyDataSetChanged();
  50.                 break;
  51.             
  52.             case HANDLER_CONNECT_TIMEOUT:
  53.                 Toast.makeText(MainActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
  54.                 break;
  55.             }
  56.         }
  57.     };
  58.     
  59.     public static final int HANDLER_CONNECT_SUCCESS = 0;
  60.     public static final int HANDLER_RECEIVE_MESSAGE = 1;
  61.     public static final int HANDLER_CONNECT_TIMEOUT = 2;
  62.     
  63.     
  64.     @Override
  65.     protected void onCreate(Bundle savedInstanceState) {
  66.         super.onCreate(savedInstanceState);
  67.         setContentView(R.layout.activity_main);
  68.         init();
  69.     }
  70.     private void init() {
  71.         etNickname = (EditText) findViewById(R.id.nickname);
  72.         etText = (EditText) findViewById(R.id.text);
  73.         connect = (Button) findViewById(R.id.connect);
  74.         lv = (ListView) findViewById(R.id.listView);
  75.     }
  76.     //发送消息
  77.     private void send() {
  78.         if(!isConnected){
  79.             Toast.makeText(this, "请先建立连接", Toast.LENGTH_SHORT).show();
  80.             return;
  81.         }
  82.         //发送消息
  83.         //获取输出流输出 网络访问需要在工作线程中完成
  84.         new Thread(){
  85.             public void run() {
  86.                 try {
  87.                     String text=etText.getText().toString();
  88.                     os.writeUTF(text);
  89.                     os.flush();
  90.                 } catch (IOException e) {
  91.                     e.printStackTrace();
  92.                 }
  93.             }
  94.         }.start();
  95.     }
  96.     // 建立连接
  97.     private void connect(){
  98.         //访问网络 须在工作线程
  99.         new Thread(){
  100.             public void run() {
  101.                 try {
  102.                     socket = new Socket();
  103.                     SocketAddress address=new InetSocketAddress("192.168.1.107", 9999);
  104.                     //3秒没有连接 将会抛出TimeOut异常
  105.                     socket.connect(address, 3000);
  106.                     is=new DataInputStream(socket.getInputStream());
  107.                     os=new DataOutputStream(socket.getOutputStream());
  108.                     //不出异常 连接建立成功发消息给handler
  109.                     handler.sendEmptyMessage(HANDLER_CONNECT_SUCCESS);
  110.                 } catch (IOException e) {
  111.                     e.printStackTrace();
  112.                     handler.sendEmptyMessage(HANDLER_CONNECT_TIMEOUT);
  113.                 }
  114.             }
  115.         }.start();
  116.     }
  117.     class Reader extends Thread {
  118.         public Reader() {
  119.         }
  120.         // 接收服务端输入进客户端的信息 并且显示在ListView中
  121.         public void run() {
  122.             try {
  123.                 while (isConnected) {
  124.                     String data = is.readUTF();
  125.                     //更新listView的数据源
  126.                     messages.add(data);
  127.                     //发消息给Handler 更新listview
  128.                     handler.sendEmptyMessage(HANDLER_RECEIVE_MESSAGE);
  129.                 }
  130.                 is.close();
  131.             } catch (IOException e) {
  132.                 e.printStackTrace();
  133.             }
  134.         }
  135.     }
  136. }

步骤六:编写doClick方法

编写MainActivity中编写doClick方法,方法为按钮的点击处理方法,根据不同的按钮控件id,分别调用connect方法和send方法。代码如下所示:

  1. package com.tarena.thread_socket_day05_cookbook_02_client;
  2. import java.io.DataInputStream;
  3. import java.io.DataOutputStream;
  4. import java.io.IOException;
  5. import java.net.InetSocketAddress;
  6. import java.net.Socket;
  7. import java.net.SocketAddress;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import android.os.Bundle;
  11. import android.os.Handler;
  12. import android.app.Activity;
  13. import android.view.Menu;
  14. import android.view.View;
  15. import android.widget.ArrayAdapter;
  16. import android.widget.Button;
  17. import android.widget.EditText;
  18. import android.widget.ListView;
  19. import android.widget.Toast;
  20. public class MainActivity extends Activity {
  21.     private EditText etNickname;
  22.     private EditText etText;
  23.     private Button connect;
  24.     private boolean isConnected;
  25.     private ListView lv;
  26.     private List<String> messages=new ArrayList<String>();
  27.     private ArrayAdapter<String> adapter;
  28.     private Socket socket;
  29.     private DataInputStream is;
  30.     private DataOutputStream os;
  31.     
  32.     private Handler handler=new Handler(){
  33.         public void handleMessage(android.os.Message msg) {
  34.             switch (msg.what) {
  35.             case HANDLER_CONNECT_SUCCESS:
  36.                 connect.setText("连接已建立");
  37.                 etNickname.setText(socket.getInetAddress().toString());
  38.                 etNickname.setEnabled(false);
  39.                 connect.setEnabled(false);
  40.                 isConnected = true;
  41.                 //把messages中的数据呈现在listView中
  42.                 adapter=new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, messages);
  43.                 lv.setAdapter(adapter);
  44.                 // 启动读取服务端返回聊天记录的线程
  45.                 new Reader().start();
  46.                 break;
  47.                 
  48.             case HANDLER_RECEIVE_MESSAGE:
  49.                 adapter.notifyDataSetChanged();
  50.                 break;
  51.             
  52.             case HANDLER_CONNECT_TIMEOUT:
  53.                 Toast.makeText(MainActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
  54.                 break;
  55.             }
  56.         }
  57.     };
  58.     
  59.     public static final int HANDLER_CONNECT_SUCCESS = 0;
  60.     public static final int HANDLER_RECEIVE_MESSAGE = 1;
  61.     public static final int HANDLER_CONNECT_TIMEOUT = 2;
  62.     
  63.     
  64.     @Override
  65.     protected void onCreate(Bundle savedInstanceState) {
  66.         super.onCreate(savedInstanceState);
  67.         setContentView(R.layout.activity_main);
  68.         init();
  69.     }
  70.     private void init() {
  71.         etNickname = (EditText) findViewById(R.id.nickname);
  72.         etText = (EditText) findViewById(R.id.text);
  73.         connect = (Button) findViewById(R.id.connect);
  74.         lv = (ListView) findViewById(R.id.listView);
  75.     }
  76.     public void doClick(View view) {
  77.         switch (view.getId()) {
  78.         case R.id.connect:
  79.             connect();
  80.             break;
  81.         
  82.         case R.id.send:
  83.             send();
  84.             break;
  85.         }
  86.     }
  87.     //发送消息
  88.     private void send() {
  89.         if(!isConnected){
  90.             Toast.makeText(this, "请先建立连接", Toast.LENGTH_SHORT).show();
  91.             return;
  92.         }
  93.         //发送消息
  94.         //获取输出流输出 网络访问需要在工作线程中完成
  95.         new Thread(){
  96.             public void run() {
  97.                 try {
  98.                     String text=etText.getText().toString();
  99.                     os.writeUTF(text);
  100.                     os.flush();
  101.                 } catch (IOException e) {
  102.                     e.printStackTrace();
  103.                 }
  104.             }
  105.         }.start();
  106.     }
  107.     // 建立连接
  108.     private void connect(){
  109.         //访问网络 须在工作线程
  110.         new Thread(){
  111.             public void run() {
  112.                 try {
  113.                     socket = new Socket();
  114.                     SocketAddress address=new InetSocketAddress("192.168.1.107", 9999);
  115.                     //3秒没有连接 将会抛出TimeOut异常
  116.                     socket.connect(address, 3000);
  117.                     is=new DataInputStream(socket.getInputStream());
  118.                     os=new DataOutputStream(socket.getOutputStream());
  119.                     //不出异常 连接建立成功发消息给handler
  120.                     handler.sendEmptyMessage(HANDLER_CONNECT_SUCCESS);
  121.                 } catch (IOException e) {
  122.                     e.printStackTrace();
  123.                     handler.sendEmptyMessage(HANDLER_CONNECT_TIMEOUT);
  124.                 }
  125.             }
  126.         }.start();
  127.     }
  128.     class Reader extends Thread {
  129.         public Reader() {
  130.         }
  131.         // 接收服务端输入进客户端的信息 并且显示在ListView中
  132.         public void run() {
  133.             try {
  134.                 while (isConnected) {
  135.                     String data = is.readUTF();
  136.                     //更新listView的数据源
  137.                     messages.add(data);
  138.                     //发消息给Handler 更新listview
  139.                     handler.sendEmptyMessage(HANDLER_RECEIVE_MESSAGE);
  140.                 }
  141.                 is.close();
  142.             } catch (IOException e) {
  143.                 e.printStackTrace();
  144.             }
  145.         }
  146.     }
  147. }

步骤七:服务端程序

创建一个java项目作为服务端程序,创建包,包中创建类Server,类中使用定义一个Vector 对象来存储所有连接上的客户端,再使用ServerSocket来监听客户端连接,连接成功后存放入Vector中。再编写一个线程内部类,用于读取客户发送消息,并遍历Vector,把消息发送给所有客户端。再main方法中开始连接监听。代码如下所示:

  1. package tarena.gsd;
  2. import java.io.DataInputStream;
  3. import java.io.DataOutputStream;
  4. import java.io.IOException;
  5. import java.net.ServerSocket;
  6. import java.net.Socket;
  7. import java.util.Vector;
  8. public class Server {
  9.     //使用Vector保存客户端的所有连接 使用方式同ArrayList 优点:线程安全
  10.     private Vector<Client> clients=new Vector<Client>();
  11.     
  12.     public void startServer() throws IOException {
  13.         ServerSocket ss=new ServerSocket(9999);
  14.         //接收客户端请求
  15.         while(true){
  16.             Socket socket=ss.accept();
  17.             Client c=new Client(socket);
  18.             clients.add(c);
  19.             c.start();
  20.         }
  21.     }
  22.     
  23.     
  24.     public static void main(String[] args) throws IOException {
  25.         Server server=new Server();
  26.         server.startServer();
  27.     }
  28.     
  29.     class Client extends Thread{
  30.         Socket socket;
  31.         DataInputStream is;
  32.         DataOutputStream os;
  33.         
  34.         public Client(Socket socket) {
  35.             this.socket=socket;
  36.             try {
  37.                 this.is=new DataInputStream(socket.getInputStream());
  38.                 this.os=new DataOutputStream(socket.getOutputStream());
  39.             } catch (IOException e) {
  40.                 e.printStackTrace();
  41.             }
  42.         }
  43.         //start后 接收客户端传进的数据 并且把这些数据输出给每一个客户端
  44.         public void run() {
  45.             boolean isLoop=true;
  46.             while(isLoop){
  47.                 //接收客户端传来的数据
  48.                 try {
  49.                     String msg=is.readUTF();
  50.                     System.out.println(msg);
  51.                     //若用户输入的exit 则退出
  52.                     if(msg.equals("exit")){
  53.                         isLoop=false; //退出循环
  54.                         socket.close();
  55.                         clients.remove(this);
  56.                         return;
  57.                     }else{
  58.                         for(int i=0; i<clients.size(); i++){
  59.                             clients.get(i).os.writeUTF(socket.getInetAddress()+"\n"+msg);
  60.                         }
  61.                     }
  62.                 } catch (IOException e) {
  63.                     e.printStackTrace();
  64.                     break;
  65.                 }
  66.             }
  67.         }
  68.     }
  69. }

2.4 完整代码

本案例布局文件的完整代码如下所示:

代码

本案例MainActivity的完整代码如下所示:

代码

本案例服务端Server类的完整代码如下所示:

代码
0 0
原创粉丝点击