android 基于socket的聊天室

来源:互联网 发布:软件著作权在线查询 编辑:程序博客网 时间:2024/05/29 18:36
Socket是TCP/IP协议上的一种通信,在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路。一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信。

Client A  发信息给 Client B ,  A的信息首先发送信息到服务器Server ,Server接受到信息后再把A的信息广播发送给所有的Clients

首先我们要在服务器建立一个ServerSocket ,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态。

Socket accept():如果接收到一个客户端Socket的连接请求,该方法将返回一个与客户端Socket对应的Socket

Server示例:

//创建一个ServerSocket,用于监听客户端Socket的连接请求ServerSocket ss = new ServerSocket(30000);//采用循环不断接受来自客户端的请求while (true){//每当接受到客户端Socket的请求,服务器端也对应产生一个SocketSocket s = ss.accept();//下面就可以使用Socket进行通信了...}

客户端通常可使用Socket的构造器来连接到指定服务器
Client示例:

//创建连接到服务器、30000端口的SocketSocket s = new Socket("192.168.2.214" , 30000);//下面就可以使用Socket进行通信了...

这样Server和Client就可以进行一个简单的通信了
当然,我们要做的是多客户,所以每当客户端Socket连接到该ServerSocket之后,程序将对应Socket加入clients集合中保存,并为该Socket启动一条线程,该线程负责处理该Socket所有的通信任务

//定义保存所有Socket的ArrayListpublic static ArrayList<Socket> clients = new ArrayList<Socket>();

当服务器线程读到客户端数据之后,程序遍历clients集合,并将该数据向clients集合中的每个Socket发送一次。这样就可以实现一个聊天室的功能了

下面来看看整个功能的demo

先建立一个Java工程,把Server.java运行起来,然后再运行手机模拟器

201112291636419120111229163704802011122916372998

服务器打印信息:

2011122916380590

程序文件结构:

2011122916502972

嘿嘿,大家别笑我,我的JAVA水平还是初学者,很多地方都觉得很菜,代码规格程度:小学。 有待提高啊!

1.先看看主Activity : SocketmsgActivity.java

public class SocketmsgActivity extends Activity {     /** Called when the activity is first created. */     private SQLiteDatabase db;          Thread thread = null;     Socket s = null;     private InetSocketAddress isa = null;       DataInputStream dis = null;     DataOutputStream dos = null;     private String reMsg=null;     private Boolean isContect = false;     private EditText chattxt;     private EditText chatbox;     private Button chatok;          private String chatKey="SLEEKNETGEOCK4stsjeS";     private String name=null,ip=null,port=null;     @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.main);         chattxt = (EditText)findViewById(R.id.chattxt);         chatbox = (EditText)findViewById(R.id.chatbox);         chatok = (Button)findViewById(R.id.chatOk);         chatbox.setCursorVisible(false);         chatbox.setFocusable(false);         chatbox.setFocusableInTouchMode(false);         chatbox.setGravity(2);                  //初始化,创建数据库来储存用户信息         InitDatabase();         db = SQLiteDatabase.openOrCreateDatabase(config.f, null);         try {             Cursor cursor = db.query("config", new String[]{"ip","name","port"},null,null, null, null, null);             while(cursor.moveToNext()){                 name = cursor.getString(cursor.getColumnIndex("name"));                 ip = cursor.getString(cursor.getColumnIndex("ip"));                 port = cursor.getString(cursor.getColumnIndex("port"));             }             cursor.close();         } catch (Exception e) {             // TODO: handle exception             System.out.println(e.toString());         }         db.close();                  //设置连接         if(ip==null || port==null){             Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);             startActivity(intent);             SocketmsgActivity.this.finish();         }         //设置名称         else if(name==null){             Intent intent = new Intent(SocketmsgActivity.this,IniuserActivity.class);             startActivity(intent);             SocketmsgActivity.this.finish();         }else{                          connect();             chatok.setOnClickListener(new View.OnClickListener() {                      @Override                 public void onClick(View v) {                          String str = chattxt.getText().toString().trim();                     System.out.println(s);                     try {                         dos.writeUTF(chatKey+"name:"+name+"end;"+str);                         chattxt.setText("");                          }catch (SocketTimeoutException  e) {                           System.out.println("連接超時,服務器未開啟或IP錯誤");                           Toast.makeText(SocketmsgActivity.this, "連接超時,服務器未開啟或IP錯誤", Toast.LENGTH_SHORT).show();                           Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);                         startActivity(intent);                         SocketmsgActivity.this.finish();                           e.printStackTrace();                       } catch (IOException e) {                         // TODO Auto-generated catch block                           System.out.println("連接超時,服務器未開啟或IP錯誤");                           Toast.makeText(SocketmsgActivity.this, "連接超時,服務器未開啟或IP錯誤", Toast.LENGTH_SHORT).show();                           Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);                         startActivity(intent);                         SocketmsgActivity.this.finish();                           e.printStackTrace();                     }                 }             });         }     }          private Runnable doThread = new Runnable() {         public void run() {             System.out.println("running!");             ReceiveMsg();         }     };             public void connect() {         try {             s = new Socket();             isa = new InetSocketAddress(ip,Integer.parseInt(port));              s.connect(isa,5000);               if(s.isConnected()){                 dos = new DataOutputStream (s.getOutputStream());                 dis = new DataInputStream (s.getInputStream());                 dos.writeUTF(chatKey+"online:"+name);                 /**                  * 这里是关键,我在此耗时8h+                  * 原因是 子线程不能直接更新UI                  * 为此,我们需要通过Handler物件,通知主线程Ui Thread来更新界面。                  *  */                 thread = new Thread(null, doThread, "Message");                   thread.start();                   System.out.println("connect");                   isContect=true;             }           }catch (UnknownHostException e) {               System.out.println("連接失敗");             Toast.makeText(SocketmsgActivity.this, "連接失敗", Toast.LENGTH_SHORT).show();             Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);             startActivity(intent);             SocketmsgActivity.this.finish();               e.printStackTrace();           }catch (SocketTimeoutException  e) {               System.out.println("連接超時,服務器未開啟或IP錯誤");               Toast.makeText(SocketmsgActivity.this, "連接超時,服務器未開啟或IP錯誤", Toast.LENGTH_SHORT).show();             Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);             startActivity(intent);             SocketmsgActivity.this.finish();               e.printStackTrace();           }catch (IOException e) {               System.out.println("連接失敗");               e.printStackTrace();           }     }         public void disConnect() {         if(dos!=null){         try {                              dos.writeUTF(chatKey+"offline:"+name);                      } catch (IOException e1) {             // TODO Auto-generated catch block             e1.printStackTrace();         }         try {             s.close();         } catch (IOException e) {               e.printStackTrace();         }         }     }             /**      * 线程监视Server信息 */     private void ReceiveMsg() {         if (isContect) {             try {                 while ((reMsg = dis.readUTF()) != null) {                     System.out.println(reMsg);                     if (reMsg != null) {                          try {                             Message msgMessage = new Message();                             msgMessage.what = 0x1981;                             handler.sendMessage(msgMessage);                             Thread.sleep(100);                         } catch (InterruptedException e) {                             // TODO Auto-generated catch block                             e.printStackTrace();                         }                      }                 }             } catch (SocketException e) {                 // TODO: handle exception                 System.out.println("exit!");             } catch (IOException e) {                 // TODO Auto-generated catch block                 e.printStackTrace();             }          }     }        /**      * 通过handler更新UI */     Handler handler = new Handler() {         public void handleMessage(Message msg) {             switch (msg.what) {             case 0x1981:                 chatbox.setText(chatbox.getText() + reMsg + '\n');                 chatbox.setSelection(chatbox.length());                 break;             }         }     };          @Override     protected void onDestroy() {         // TODO Auto-generated method stub         super.onDestroy();         disConnect();         //System.exit(0);     }          @Override     public boolean onCreateOptionsMenu(Menu menu) {         // TODO Auto-generated method stub         menu.add(0, 1, 1, "初始化設置");         menu.add(0, 2, 2, "退出");         return super.onCreateOptionsMenu(menu);     }      @Override     public boolean onOptionsItemSelected(MenuItem item) {         // TODO Auto-generated method stub         if(item.getItemId()==1){             Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);             startActivity(intent);             SocketmsgActivity.this.finish();         }else if(item.getItemId()==2){             disConnect();             SocketmsgActivity.this.finish();               android.os.Process.killProcess(android.os.Process.myPid());             System.exit(0);         }         return super.onOptionsItemSelected(item);     }      public void InitDatabase(){                   if(!config.path.exists()){               config.path.mkdirs();                 Log.i("LogDemo", "mkdir");           }            if(!config.f.exists()){                   try{                    config.f.createNewFile();                   Log.i("LogDemo", "create a new database file");             }catch(IOException e){                    Log.i("LogDemo",e.toString());             }            }           try {             if(tabIsExist("config")==false){                 db = SQLiteDatabase.openOrCreateDatabase(config.f, null);                   db.execSQL("create table config(_id integer primary key autoincrement," +                         "ip varchar(128),port varchar(10),name varchar(32))");                 Log.i("LogDemo", "create a database");                 db.close();             }         } catch (Exception e) {             // TODO: handle exception             Log.i("LogDemo",e.toString());         }     }          /**      * check the database is already exist      * @param tabName      * @return */     public boolean tabIsExist(String tabName){         boolean result = false;         if(tabName == null){                 return false;         }         Cursor cursor = null;         db = SQLiteDatabase.openOrCreateDatabase(config.f, null);          try {             String sql = "select count(*) as c from sqlite_master where type ='table' " +                         "and name ='"+tabName.trim()+"' ";             cursor = db.rawQuery(sql, null);             if(cursor.moveToNext()){                 int count = cursor.getInt(0);                 if(count>0){                     result = true;                 }             }                          } catch (Exception e) {                 // TODO: handle exception         }           cursor.close();         db.close();         return result;     } }

2.初始化IP和端口Activity, IniActivity.java

public class IniActivity extends Activity{      private EditText ip,port;     private Button nextButton;     private String getip,getport;     private ProgressDialog progressDialog;     private InetSocketAddress isa = null;      private SQLiteDatabase db;     private String ipstring=null,portString=null;     private int row=0;     @Override     protected void onCreate(Bundle savedInstanceState) {         // TODO Auto-generated method stub         super.onCreate(savedInstanceState);         setContentView(R.layout.config);                  ip = (EditText)findViewById(R.id.ip);         port = (EditText)findViewById(R.id.port);         nextButton = (Button)findViewById(R.id.next);                           db = SQLiteDatabase.openOrCreateDatabase(config.f, null);         try {             Cursor cursor = db.query("config", new String[]{"ip","port"},null,null, null, null, null);             while(cursor.moveToNext()){                 ipstring = cursor.getString(cursor.getColumnIndex("ip"));                 portString = cursor.getString(cursor.getColumnIndex("port"));                 row++;             }             ip.setText(ipstring);             port.setText(portString);             cursor.close();         } catch (Exception e) {             // TODO: handle exception             System.out.println(e.toString());         }         db.close();                  nextButton.setOnClickListener(new nextButtonListenner());     }          class nextButtonListenner implements OnClickListener{          @Override         public void onClick(View v) {             // TODO Auto-generated method stub             getip = ip.getText().toString().trim();             getport = port.getText().toString().trim();             if(getip=="" || getip==null || getip.equals("")){                 Toast.makeText(IniActivity.this, "請輸入IP", Toast.LENGTH_SHORT).show();                 ip.setFocusable(true);             }else if(getport=="" || getport==null || getport.equals("")){                 Toast.makeText(IniActivity.this, "請輸入端口", Toast.LENGTH_SHORT).show();                 port.setFocusable(true);             }else{             //progressDialog = ProgressDialog.show(IniActivity.this, "", "請稍後...", true, false); //new Thread() { //@Override //public void run() {                     try {                         Socket s = new Socket();                         isa = new InetSocketAddress(getip,Integer.parseInt(getport));                          s.connect(isa,5000);                          //showDialog("連接成功",IniActivity.this);                         try {                             //生成ContentValues对象                             ContentValues values = new ContentValues();                             //想该对象当中插入键值对,其中键是列名,值是希望插入到这一列的值,值必须和数据库当中的数据类型一致                             values.put("ip", getip);                             values.put("port",getport);                             db = SQLiteDatabase.openOrCreateDatabase(config.f, null);                              if(row==0){                                 db.insert("config", null, values);                             }else{                                 db.update("config", values ,null,null);                             }                             Toast.makeText(IniActivity.this, "連接成功", Toast.LENGTH_SHORT);                             s.close();                             Intent intent = new Intent(IniActivity.this,IniuserActivity.class);                             startActivity(intent);                             IniActivity.this.finish();                             db.close();                         } catch (Exception e) {                             // TODO: handle exception                             showDialog("設置失敗,數據庫不可用",IniActivity.this);                         }                                                                       } catch (UnknownHostException e) {                         // TODO Auto-generated catch block                         e.printStackTrace();                         showDialog("連接失敗,IP或者端口不可用",IniActivity.this);                     }catch (SocketTimeoutException  e) {                           System.out.println("連接超時,服務器未開啟或IP錯誤");                           showDialog("連接超時,服務器未開啟或IP錯誤",IniActivity.this);                           e.printStackTrace();                     }                     catch (IOException e) {                         // TODO Auto-generated catch block                         e.printStackTrace();                         showDialog("連接失敗,IP或者端口不可用",IniActivity.this);                     }                     //progressDialog.dismiss(); //finish(); //} //}.start();             }                      }              }          /**      * define a dialog for show the message      * @param mess      * @param activity */     public void showDialog(String mess,Activity activity){       new AlertDialog.Builder(activity).setTitle("信息")        .setMessage(mess)        .setNegativeButton("確定",new DialogInterface.OnClickListener()        {          public void onClick(DialogInterface dialog, int which)          {                    }        })        .show();     } }

3.初始化用户名称Activity, IniuserActivity.java

public class IniuserActivity extends Activity{     private EditText name;     private Button ok;     private SQLiteDatabase db;          private String nameString;     @Override     protected void onCreate(Bundle savedInstanceState) {         // TODO Auto-generated method stub         super.onCreate(savedInstanceState);         setContentView(R.layout.configuser);                  name = (EditText)findViewById(R.id.name);         ok = (Button)findViewById(R.id.ok);         ok.setOnClickListener(new okButtonListenner());                           db = SQLiteDatabase.openOrCreateDatabase(config.f, null);         try {             Cursor cursor = db.query("config", new String[]{"name"},null,null, null, null, null);             while(cursor.moveToNext()){                 nameString = cursor.getString(cursor.getColumnIndex("name"));             }             name.setText(nameString);             cursor.close();         } catch (Exception e) {             // TODO: handle exception             System.out.println(e.toString());         }         db.close();     }          class okButtonListenner implements OnClickListener{          @Override         public void onClick(View v) {             // TODO Auto-generated method stub             String getname = name.getText().toString().trim();             if(getname==""){                 Toast.makeText(IniuserActivity.this, "請輸入您的稱呢", Toast.LENGTH_SHORT).show();                 name.setFocusable(true);             }else{                             try {                     //生成ContentValues对象                     ContentValues values = new ContentValues();                     //想该对象当中插入键值对,其中键是列名,值是希望插入到这一列的值,值必须和数据库当中的数据类型一致                     values.put("name", getname);                     db = SQLiteDatabase.openOrCreateDatabase(config.f, null);                      db.update("config",values,null,null);                     Toast.makeText(IniuserActivity.this, "設置完成", Toast.LENGTH_SHORT).show();                     Intent intent = new Intent(IniuserActivity.this,SocketmsgActivity.class);                     startActivity(intent);                     IniuserActivity.this.finish();                     db.close();                 } catch (Exception e) {                     // TODO: handle exception                     showDialog("設置失敗,數據庫不可用",IniuserActivity.this);                 }             }         }              }          /**      * define a dialog for show the message      * @param mess      * @param activity */     public void showDialog(String mess,Activity activity){       new AlertDialog.Builder(activity).setTitle("信息")        .setMessage(mess)        .setNegativeButton("確定",new DialogInterface.OnClickListener()        {          public void onClick(DialogInterface dialog, int which)          {                    }        })        .show();     } }

4.config.java

public class config{    public static String SDCARD = android.os.Environment.getExternalStorageDirectory().getAbsolutePath();    public static File path = new File(SDCARD+"/RunChatDatabase/"); //数据库文件目录       public static File f = new File(SDCARD+"/RunChatDatabase/config.db"); //数据库文件  }

布局文件:

1.main.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:orientation="vertical" android:layout_width="fill_parent"     android:layout_height="fill_parent">     <EditText android:id="@+id/chatbox" android:layout_width="fill_parent"         android:layout_height="fill_parent" android:layout_weight="1">     </EditText>     <EditText android:id="@+id/chattxt" android:layout_width="fill_parent"         android:layout_height="wrap_content" android:gravity="top"         android:hint="你想和对方说点什么?">     </EditText>     <Button android:id="@+id/chatOk" android:layout_width="fill_parent"         android:layout_height="wrap_content" android:text="Send"          android:textSize="@dimen/btn1">     </Button>  </LinearLayout>

2.config.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:orientation="vertical" android:layout_width="fill_parent"     android:layout_height="fill_parent">     <EditText android:id="@+id/chatbox" android:layout_width="fill_parent"         android:layout_height="fill_parent" android:layout_weight="1">     </EditText>     <EditText android:id="@+id/chattxt" android:layout_width="fill_parent"         android:layout_height="wrap_content" android:gravity="top"         android:hint="你想和对方说点什么?">     </EditText>     <Button android:id="@+id/chatOk" android:layout_width="fill_parent"         android:layout_height="wrap_content" android:text="Send"          android:textSize="@dimen/btn1">     </Button>  </LinearLayout>

3.configuer.xml

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"     android:orientation="vertical" android:layout_width="fill_parent"     android:layout_height="fill_parent">     <EditText android:id="@+id/chatbox" android:layout_width="fill_parent"         android:layout_height="fill_parent" android:layout_weight="1">     </EditText>     <EditText android:id="@+id/chattxt" android:layout_width="fill_parent"         android:layout_height="wrap_content" android:gravity="top"         android:hint="你想和对方说点什么?">     </EditText>     <Button android:id="@+id/chatOk" android:layout_width="fill_parent"         android:layout_height="wrap_content" android:text="Send"          android:textSize="@dimen/btn1">     </Button>  </LinearLayout>

style文件:dimens.xml

<?xml version="1.0" encoding="utf-8"?> <resources>          <dimen name="h3">30dip</dimen>      <dimen name="h2">40dip</dimen>      <dimen name="btn1">30dip</dimen>     <dimen name="et1">25dip</dimen> </resources>

最后是服务器文件:Server.java

import java.io.*; import java.net.*; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*;  import javax.sound.sampled.Port; import javax.swing.JOptionPane;  public class Server {          ServerSocket ss = null;     private String getnameString=null;     boolean started = false;     List<Client> clients = new ArrayList<Client>();     List<Info> infos = new ArrayList<Info>();     public static void main(String[] args) {         String inputport = JOptionPane.showInputDialog("請輸入該服務器使用的端口:");         int port = Integer.parseInt(inputport);         new Server().start(port);     }       public void start(int port) {         try {            ss = new ServerSocket(port);            System.out.println("服務器啟動");            started = true;         } catch (BindException e) {               System.out.println(" 端口已经被占用");               System.exit(0);            }           catch (IOException e) {              e.printStackTrace();           }        try {          while (started) {              Socket s = ss.accept();              Client c = new Client (s);              System.out.println("a client is connected");              new Thread(c).start();              clients.add(c);                                      }       } catch (IOException e) {             e.printStackTrace();          }          finally {             try {                ss.close();             } catch (IOException e) {                   e.printStackTrace();                }          }    }    public List<Client> getClient(){        return clients;    }    class Client implements Runnable {      private String chatKey="SLEEKNETGEOCK4stsjeS";      private Socket s = null;      private DataInputStream dis = null;      private DataOutputStream dos = null;      private boolean bConnected = false;      private String sendmsg=null;      Client (Socket s) {         this.s = s;         try {           dis = new DataInputStream (s.getInputStream());           dos = new DataOutputStream (s.getOutputStream());           bConnected = true;         } catch(IOException e) {               e.printStackTrace();            }      }            public void send (String str) {                    try {              //System.out.println(s);              dos.writeUTF(str+"");              dos.flush();          } catch(IOException e) {              clients.remove(this);              System.out.println("对方已经退出了");          }      }      public void run() {          try {             while (bConnected) {                 String str = dis.readUTF();                 DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");                 String date = "  ["+df.format(new Date())+"]";                 if(str.startsWith(chatKey+"online:")){                     Info info = new Info();                     getnameString = str.substring(27);                                          info.setName(getnameString);                     infos.add(info);                     for (int i=0; i<clients.size(); i++) {                       Client c = clients.get(i);                       c.send(getnameString+" on line."+date);                     }                     System.out.println(getnameString+" on line."+date);                 }else if(str.startsWith(chatKey+"offline:")){                     getnameString = str.substring(28);                     clients.remove(this);                     for (int i=0; i<clients.size(); i++) {                           Client c = clients.get(i);                           c.send(getnameString+" off line."+date);                         }                     System.out.println(getnameString+" off line."+date);                 }                 else{                     int charend = str.indexOf("end;");                     String chatString = str.substring(charend+4);                     String chatName = str.substring(25, charend);                                          sendmsg=chatName+date+"\n"+chatString;                      for (int i=0; i<clients.size(); i++) {                         Client c = clients.get(i);                         c.send(sendmsg);                       }                     System.out.println(sendmsg);                 }              }          } catch (SocketException e) {              System.out.println("client is closed!");              clients.remove(this);          } catch (EOFException e) {                System.out.println("client is closed!");                clients.remove(this);             }             catch (IOException e) {                e.printStackTrace();             }            finally {              try {                if (dis != null) dis.close();                if (dos != null) dos.close();                if (s != null) s.close();              } catch (IOException e) {                    e.printStackTrace();                }            }      }   }      class Info{       private String info_name = null;       public Info(){                  }       public void setName(String name){           info_name = name;       }       public String getName(){           return info_name;       }   } }

以上只是一个粗略的聊天室功能,如果要实现私聊,还需要保存该Socket关联的客户信息。一个客户端可以将信息发送另一个指定客户端

原创粉丝点击