Android 基于Socket的聊天室

http://www.cnblogs.com/-run/archive/2011/12/29/2306363.html


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的请求,服务器端也对应产生一个Socket
Socket s = ss.accept();
//下面就可以使用Socket进行通信了
...
}
复制代码

 

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

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


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

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

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

 

 

下面来看看整个功能的demo

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

 

   

服务器打印信息:

 

程序文件结构:


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

 

1.先看看主Activity : SocketmsgActivity.java

复制代码
  1 public class SocketmsgActivity extends Activity {
  2     /** Called when the activity is first created. */
  3     private SQLiteDatabase db;
  4     
  5     Thread thread = null;
  6     Socket s = null;
  7     private InetSocketAddress isa = null; 
  8 
  9     DataInputStream dis = null;
 10     DataOutputStream dos = null;
 11     private String reMsg=null;
 12     private Boolean isContect = false;
 13     private EditText chattxt;
 14     private EditText chatbox;
 15     private Button chatok;
 16     
 17     private String chatKey="SLEEKNETGEOCK4stsjeS";
 18     private String name=null,ip=null,port=null;
 19     @Override
 20     public void onCreate(Bundle savedInstanceState) {
 21         super.onCreate(savedInstanceState);
 22         setContentView(R.layout.main);
 23         chattxt = (EditText)findViewById(R.id.chattxt);
 24         chatbox = (EditText)findViewById(R.id.chatbox);
 25         chatok = (Button)findViewById(R.id.chatOk);
 26         chatbox.setCursorVisible(false);
 27         chatbox.setFocusable(false);
 28         chatbox.setFocusableInTouchMode(false);
 29         chatbox.setGravity(2);
 30         
 31         //初始化,创建数据库来储存用户信息
 32         InitDatabase();
 33         db = SQLiteDatabase.openOrCreateDatabase(config.f, null);
 34         try {
 35             Cursor cursor = db.query("config", new String[]{"ip","name","port"},null,null, null, null, null);
 36             while(cursor.moveToNext()){
 37                 name = cursor.getString(cursor.getColumnIndex("name"));
 38                 ip = cursor.getString(cursor.getColumnIndex("ip"));
 39                 port = cursor.getString(cursor.getColumnIndex("port"));
 40             }
 41             cursor.close();
 42         } catch (Exception e) {
 43             // TODO: handle exception
 44             System.out.println(e.toString());
 45         }
 46         db.close();
 47         
 48         //设置连接
 49         if(ip==null || port==null){
 50             Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);
 51             startActivity(intent);
 52             SocketmsgActivity.this.finish();
 53         }
 54         //设置名称
 55         else if(name==null){
 56             Intent intent = new Intent(SocketmsgActivity.this,IniuserActivity.class);
 57             startActivity(intent);
 58             SocketmsgActivity.this.finish();
 59         }else{
 60             
 61             connect();
 62             chatok.setOnClickListener(new View.OnClickListener() {
 63     
 64                 @Override
 65                 public void onClick(View v) {
 66     
 67                     String str = chattxt.getText().toString().trim();
 68                     System.out.println(s);
 69                     try {
 70                         dos.writeUTF(chatKey+"name:"+name+"end;"+str);
 71                         chattxt.setText("");
 72     
 73                     }catch (SocketTimeoutException  e) {
 74                           System.out.println("連接超時,服務器未開啟或IP錯誤");
 75                           Toast.makeText(SocketmsgActivity.this, "連接超時,服務器未開啟或IP錯誤", Toast.LENGTH_SHORT).show();
 76                           Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);
 77                         startActivity(intent);
 78                         SocketmsgActivity.this.finish();
 79                           e.printStackTrace();
 80                       } catch (IOException e) {
 81                         // TODO Auto-generated catch block
 82                           System.out.println("連接超時,服務器未開啟或IP錯誤");
 83                           Toast.makeText(SocketmsgActivity.this, "連接超時,服務器未開啟或IP錯誤", Toast.LENGTH_SHORT).show();
 84                           Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);
 85                         startActivity(intent);
 86                         SocketmsgActivity.this.finish();
 87                           e.printStackTrace();
 88                     }
 89                 }
 90             });
 91         }
 92     }
 93     
 94     private Runnable doThread = new Runnable() {
 95         public void run() {
 96             System.out.println("running!");
 97             ReceiveMsg();
 98         }
 99     };   
100     
101     public void connect() {
102         try {
103             s = new Socket();
104             isa = new InetSocketAddress(ip,Integer.parseInt(port)); 
105             s.connect(isa,5000); 
106 
107             if(s.isConnected()){
108                 dos = new DataOutputStream (s.getOutputStream());
109                 dis = new DataInputStream (s.getInputStream());
110                 dos.writeUTF(chatKey+"online:"+name);
111                 /**
112                  * 这里是关键,我在此耗时8h+
113                  * 原因是 子线程不能直接更新UI
114                  * 为此,我们需要通过Handler物件,通知主线程Ui Thread来更新界面。
115                  * 
116 */
117                 thread = new Thread(null, doThread, "Message");
118                   thread.start();
119                   System.out.println("connect");
120                   isContect=true;
121             }
122           }catch (UnknownHostException e) {
123               System.out.println("連接失敗");
124             Toast.makeText(SocketmsgActivity.this, "連接失敗", Toast.LENGTH_SHORT).show();
125             Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);
126             startActivity(intent);
127             SocketmsgActivity.this.finish();
128               e.printStackTrace();
129           }catch (SocketTimeoutException  e) {
130               System.out.println("連接超時,服務器未開啟或IP錯誤");
131               Toast.makeText(SocketmsgActivity.this, "連接超時,服務器未開啟或IP錯誤", Toast.LENGTH_SHORT).show();
132             Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);
133             startActivity(intent);
134             SocketmsgActivity.this.finish();
135               e.printStackTrace();
136           }catch (IOException e) {
137               System.out.println("連接失敗");
138               e.printStackTrace();
139           }
140     }
141    
142     public void disConnect() {
143         if(dos!=null){
144         try {
145             
146                 dos.writeUTF(chatKey+"offline:"+name);
147             
148         } catch (IOException e1) {
149             // TODO Auto-generated catch block
150             e1.printStackTrace();
151         }
152         try {
153             s.close();
154         } catch (IOException e) {
155               e.printStackTrace();
156         }
157         }
158     }
159   
160     
161     /**
162      * 线程监视Server信息
163 */
164     private void ReceiveMsg() {
165         if (isContect) {
166             try {
167                 while ((reMsg = dis.readUTF()) != null) {
168                     System.out.println(reMsg);
169                     if (reMsg != null) {
170 
171                         try {
172                             Message msgMessage = new Message();
173                             msgMessage.what = 0x1981;
174                             handler.sendMessage(msgMessage);
175                             Thread.sleep(100);
176                         } catch (InterruptedException e) {
177                             // TODO Auto-generated catch block
178                             e.printStackTrace();
179                         }
180 
181                     }
182                 }
183             } catch (SocketException e) {
184                 // TODO: handle exception
185                 System.out.println("exit!");
186             } catch (IOException e) {
187                 // TODO Auto-generated catch block
188                 e.printStackTrace();
189             }
190 
191         }
192     }
193   
194     /**
195      * 通过handler更新UI
196 */
197     Handler handler = new Handler() {
198         public void handleMessage(Message msg) {
199             switch (msg.what) {
200             case 0x1981:
201                 chatbox.setText(chatbox.getText() + reMsg + '\n');
202                 chatbox.setSelection(chatbox.length());
203                 break;
204             }
205         }
206     };
207     
208     @Override
209     protected void onDestroy() {
210         // TODO Auto-generated method stub
211         super.onDestroy();
212         disConnect();
213         //System.exit(0);
214     }
215     
216     @Override
217     public boolean onCreateOptionsMenu(Menu menu) {
218         // TODO Auto-generated method stub
219         menu.add(0, 1, 1, "初始化設置");
220         menu.add(0, 2, 2, "退出");
221         return super.onCreateOptionsMenu(menu);
222     }
223 
224     @Override
225     public boolean onOptionsItemSelected(MenuItem item) {
226         // TODO Auto-generated method stub
227         if(item.getItemId()==1){
228             Intent intent = new Intent(SocketmsgActivity.this,IniActivity.class);
229             startActivity(intent);
230             SocketmsgActivity.this.finish();
231         }else if(item.getItemId()==2){
232             disConnect();
233             SocketmsgActivity.this.finish();  
234             android.os.Process.killProcess(android.os.Process.myPid());
235             System.exit(0);
236         }
237         return super.onOptionsItemSelected(item);
238     }
239 
240     public void InitDatabase(){
241          
242         if(!config.path.exists()){  
243             config.path.mkdirs();    
244             Log.i("LogDemo", "mkdir");  
245         }   
246         if(!config.f.exists()){      
247             try{   
248                 config.f.createNewFile();  
249                 Log.i("LogDemo", "create a new database file");
250             }catch(IOException e){   
251                 Log.i("LogDemo",e.toString());
252             }   
253         }  
254         try {
255             if(tabIsExist("config")==false){
256                 db = SQLiteDatabase.openOrCreateDatabase(config.f, null);  
257                 db.execSQL("create table config(_id integer primary key autoincrement," +
258                         "ip varchar(128),port varchar(10),name varchar(32))");
259                 Log.i("LogDemo", "create a database");
260                 db.close();
261             }
262         } catch (Exception e) {
263             // TODO: handle exception
264             Log.i("LogDemo",e.toString());
265         }
266     }
267     
268     /**
269      * check the database is already exist
270      * @param tabName
271      * @return
272 */
273     public boolean tabIsExist(String tabName){
274         boolean result = false;
275         if(tabName == null){
276                 return false;
277         }
278         Cursor cursor = null;
279         db = SQLiteDatabase.openOrCreateDatabase(config.f, null); 
280         try {
281             String sql = "select count(*) as c from sqlite_master where type ='table' " +
282                         "and name ='"+tabName.trim()+"' ";
283             cursor = db.rawQuery(sql, null);
284             if(cursor.moveToNext()){
285                 int count = cursor.getInt(0);
286                 if(count>0){
287                     result = true;
288                 }
289             }
290                 
291         } catch (Exception e) {
292                 // TODO: handle exception
293         }  
294         cursor.close();
295         db.close();
296         return result;
297     }
298 }
复制代码

 

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

IniActivity.java

 

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

IniuserActivity.java

 

4.config.java

config.java

 

布局文件:

1.main.xml

main.xml


2.config.xml

config.xml

3.configuer.xml

configuser.xml

 

style文件:dimens.xml

dimens.xml

 

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

复制代码
  1 import java.io.*;
  2 import java.net.*;
  3 import java.text.DateFormat;
  4 import java.text.SimpleDateFormat;
  5 import java.util.*;
  6 
  7 import javax.sound.sampled.Port;
  8 import javax.swing.JOptionPane;
  9 
 10 public class Server {
 11     
 12     ServerSocket ss = null;
 13     private String getnameString=null;
 14     boolean started = false;
 15     List<Client> clients = new ArrayList<Client>();
 16     List<Info> infos = new ArrayList<Info>();
 17     public static void main(String[] args) {
 18         String inputport = JOptionPane.showInputDialog("請輸入該服務器使用的端口:");
 19         int port = Integer.parseInt(inputport);
 20         new Server().start(port);
 21     }
 22  
 23     public void start(int port) {
 24         try {
 25            ss = new ServerSocket(port);
 26            System.out.println("服務器啟動");
 27            started = true;
 28         } catch (BindException e) {
 29               System.out.println(" 端口已经被占用");
 30               System.exit(0);
 31            }
 32           catch (IOException e) {
 33              e.printStackTrace();
 34           }
 35 
 36       try {
 37          while (started) {
 38              Socket s = ss.accept();
 39              Client c = new Client (s);
 40              System.out.println("a client is connected");
 41              new Thread(c).start();
 42              clients.add(c);
 43              
 44              
 45          }
 46       } catch (IOException e) {
 47             e.printStackTrace();
 48          }
 49          finally {
 50             try {
 51                ss.close();
 52             } catch (IOException e) {
 53                   e.printStackTrace();
 54                }
 55          }
 56    }
 57    public List<Client> getClient(){
 58        return clients;
 59    }
 60 
 61   class Client implements Runnable {
 62      private String chatKey="SLEEKNETGEOCK4stsjeS";
 63      private Socket s = null;
 64      private DataInputStream dis = null;
 65      private DataOutputStream dos = null;
 66      private boolean bConnected = false;
 67      private String sendmsg=null;
 68      Client (Socket s) {
 69         this.s = s;
 70         try {
 71           dis = new DataInputStream (s.getInputStream());
 72           dos = new DataOutputStream (s.getOutputStream());
 73           bConnected = true;
 74         } catch(IOException e) {
 75               e.printStackTrace();
 76            }
 77      }
 78      
 79      public void send (String str) {
 80          
 81          try {
 82              //System.out.println(s);
 83              dos.writeUTF(str+"");
 84              dos.flush();
 85          } catch(IOException e) {
 86              clients.remove(this);
 87              System.out.println("对方已经退出了");
 88          }
 89      }
 90      public void run() {
 91          try {
 92             while (bConnected) {
 93                 String str = dis.readUTF();
 94                 DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
 95                 String date = "  ["+df.format(new Date())+"]";
 96                 if(str.startsWith(chatKey+"online:")){
 97                     Info info = new Info();
 98                     getnameString = str.substring(27);
 99                     
100                     info.setName(getnameString);
101                     infos.add(info);
102                     for (int i=0; i<clients.size(); i++) {
103                       Client c = clients.get(i);
104                       c.send(getnameString+" on line."+date);
105                     }
106                     System.out.println(getnameString+" on line."+date);
107                 }else if(str.startsWith(chatKey+"offline:")){
108                     getnameString = str.substring(28);
109                     clients.remove(this);
110                     for (int i=0; i<clients.size(); i++) {
111                           Client c = clients.get(i);
112                           c.send(getnameString+" off line."+date);
113                         }
114                     System.out.println(getnameString+" off line."+date);
115                 }
116                 else{
117                     int charend = str.indexOf("end;");
118                     String chatString = str.substring(charend+4);
119                     String chatName = str.substring(25, charend);
120                     
121                     sendmsg=chatName+date+"\n"+chatString; 
122                     for (int i=0; i<clients.size(); i++) {
123                         Client c = clients.get(i);
124                         c.send(sendmsg);
125                       }
126                     System.out.println(sendmsg);
127                 }
128              }
129          } catch (SocketException e) {
130              System.out.println("client is closed!");
131              clients.remove(this);
132          } catch (EOFException e) {
133                System.out.println("client is closed!");
134                clients.remove(this);
135             }
136             catch (IOException e) {
137                e.printStackTrace();
138             }
139            finally {
140              try {
141                if (dis != null) dis.close();
142                if (dos != null) dos.close();
143                if (s != null) s.close();
144              } catch (IOException e) {
145                    e.printStackTrace();
146                }
147            }
148      }
149   }
150   
151   class Info{
152       private String info_name = null;
153       public Info(){
154           
155       }
156       public void setName(String name){
157           info_name = name;
158       }
159       public String getName(){
160           return info_name;
161       }
162   }
163 }
复制代码

 

以上只是一个粗略的聊天室功能,如果要实现私聊,还需要保存该Socket关联的客户信息。一个客户端可以将信息发送另一个指定客户端。实际上,我们知道所有客户端只与服务器连接,客户端之间并没有互相连接。这个功能等我以后有时间再写个demo.....


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值