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
3.初始化用户名称Activity, IniuserActivity.java
4.config.java
布局文件:
1.main.xml
2.config.xml
3.configuer.xml
style文件: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.....