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
1 public class IniActivity extends Activity{
2
3 private EditText ip,port;
4 private Button nextButton;
5 private String getip,getport;
6 private ProgressDialog progressDialog;
7 private InetSocketAddress isa = null;
8 private SQLiteDatabase db;
9 private String ipstring=null,portString=null;
10 private int row=0;
11 @Override
12 protected void onCreate(Bundle savedInstanceState) {
13 // TODO Auto-generated method stub
14 super.onCreate(savedInstanceState);
15 setContentView(R.layout.config);
16
17 ip = (EditText)findViewById(R.id.ip);
18 port = (EditText)findViewById(R.id.port);
19 nextButton = (Button)findViewById(R.id.next);
20
21
22 db = SQLiteDatabase.openOrCreateDatabase(config.f, null);
23 try {
24 Cursor cursor = db.query("config", new String[]{"ip","port"},null,null, null, null, null);
25 while(cursor.moveToNext()){
26 ipstring = cursor.getString(cursor.getColumnIndex("ip"));
27 portString = cursor.getString(cursor.getColumnIndex("port"));
28 row++;
29 }
30 ip.setText(ipstring);
31 port.setText(portString);
32 cursor.close();
33 } catch (Exception e) {
34 // TODO: handle exception
35 System.out.println(e.toString());
36 }
37 db.close();
38
39 nextButton.setOnClickListener(new nextButtonListenner());
40 }
41
42 class nextButtonListenner implements OnClickListener{
43
44 @Override
45 public void onClick(View v) {
46 // TODO Auto-generated method stub
47 getip = ip.getText().toString().trim();
48 getport = port.getText().toString().trim();
49 if(getip=="" || getip==null || getip.equals("")){
50 Toast.makeText(IniActivity.this, "請輸入IP", Toast.LENGTH_SHORT).show();
51 ip.setFocusable(true);
52 }else if(getport=="" || getport==null || getport.equals("")){
53 Toast.makeText(IniActivity.this, "請輸入端口", Toast.LENGTH_SHORT).show();
54 port.setFocusable(true);
55 }else{
56 //progressDialog = ProgressDialog.show(IniActivity.this, "", "請稍後...", true, false);
57 //new Thread() {
58 //@Override
59 //public void run() {
60 try {
61 Socket s = new Socket();
62 isa = new InetSocketAddress(getip,Integer.parseInt(getport));
63 s.connect(isa,5000);
64 //showDialog("連接成功",IniActivity.this);
65 try {
66 //生成ContentValues对象
67 ContentValues values = new ContentValues();
68 //想该对象当中插入键值对,其中键是列名,值是希望插入到这一列的值,值必须和数据库当中的数据类型一致
69 values.put("ip", getip);
70 values.put("port",getport);
71 db = SQLiteDatabase.openOrCreateDatabase(config.f, null);
72 if(row==0){
73 db.insert("config", null, values);
74 }else{
75 db.update("config", values ,null,null);
76 }
77 Toast.makeText(IniActivity.this, "連接成功", Toast.LENGTH_SHORT);
78 s.close();
79 Intent intent = new Intent(IniActivity.this,IniuserActivity.class);
80 startActivity(intent);
81 IniActivity.this.finish();
82 db.close();
83 } catch (Exception e) {
84 // TODO: handle exception
85 showDialog("設置失敗,數據庫不可用",IniActivity.this);
86 }
87
88
89 } catch (UnknownHostException e) {
90 // TODO Auto-generated catch block
91 e.printStackTrace();
92 showDialog("連接失敗,IP或者端口不可用",IniActivity.this);
93 }catch (SocketTimeoutException e) {
94 System.out.println("連接超時,服務器未開啟或IP錯誤");
95 showDialog("連接超時,服務器未開啟或IP錯誤",IniActivity.this);
96 e.printStackTrace();
97 }
98 catch (IOException e) {
99 // TODO Auto-generated catch block
100 e.printStackTrace();
101 showDialog("連接失敗,IP或者端口不可用",IniActivity.this);
102 }
103 //progressDialog.dismiss();
104 //finish();
105 //}
106 //}.start();
107 }
108
109 }
110
111 }
112
113 /**
114 * define a dialog for show the message
115 * @param mess
116 * @param activity
117 */
118 public void showDialog(String mess,Activity activity){
119 new AlertDialog.Builder(activity).setTitle("信息")
120 .setMessage(mess)
121 .setNegativeButton("確定",new DialogInterface.OnClickListener()
122 {
123 public void onClick(DialogInterface dialog, int which)
124 {
125 }
126 })
127 .show();
128 }
129 }
3.初始化用户名称Activity, IniuserActivity.java
1 public class IniuserActivity extends Activity{
2 private EditText name;
3 private Button ok;
4 private SQLiteDatabase db;
5
6 private String nameString;
7 @Override
8 protected void onCreate(Bundle savedInstanceState) {
9 // TODO Auto-generated method stub
10 super.onCreate(savedInstanceState);
11 setContentView(R.layout.configuser);
12
13 name = (EditText)findViewById(R.id.name);
14 ok = (Button)findViewById(R.id.ok);
15 ok.setOnClickListener(new okButtonListenner());
16
17
18 db = SQLiteDatabase.openOrCreateDatabase(config.f, null);
19 try {
20 Cursor cursor = db.query("config", new String[]{"name"},null,null, null, null, null);
21 while(cursor.moveToNext()){
22 nameString = cursor.getString(cursor.getColumnIndex("name"));
23 }
24 name.setText(nameString);
25 cursor.close();
26 } catch (Exception e) {
27 // TODO: handle exception
28 System.out.println(e.toString());
29 }
30 db.close();
31 }
32
33 class okButtonListenner implements OnClickListener{
34
35 @Override
36 public void onClick(View v) {
37 // TODO Auto-generated method stub
38 String getname = name.getText().toString().trim();
39 if(getname==""){
40 Toast.makeText(IniuserActivity.this, "請輸入您的稱呢", Toast.LENGTH_SHORT).show();
41 name.setFocusable(true);
42 }else{
43 try {
44 //生成ContentValues对象
45 ContentValues values = new ContentValues();
46 //想该对象当中插入键值对,其中键是列名,值是希望插入到这一列的值,值必须和数据库当中的数据类型一致
47 values.put("name", getname);
48 db = SQLiteDatabase.openOrCreateDatabase(config.f, null);
49 db.update("config",values,null,null);
50 Toast.makeText(IniuserActivity.this, "設置完成", Toast.LENGTH_SHORT).show();
51 Intent intent = new Intent(IniuserActivity.this,SocketmsgActivity.class);
52 startActivity(intent);
53 IniuserActivity.this.finish();
54 db.close();
55 } catch (Exception e) {
56 // TODO: handle exception
57 showDialog("設置失敗,數據庫不可用",IniuserActivity.this);
58 }
59 }
60 }
61
62 }
63
64 /**
65 * define a dialog for show the message
66 * @param mess
67 * @param activity
68 */
69 public void showDialog(String mess,Activity activity){
70 new AlertDialog.Builder(activity).setTitle("信息")
71 .setMessage(mess)
72 .setNegativeButton("確定",new DialogInterface.OnClickListener()
73 {
74 public void onClick(DialogInterface dialog, int which)
75 {
76 }
77 })
78 .show();
79 }
80 }
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
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:orientation="vertical" android:layout_width="fill_parent"
4 android:layout_height="fill_parent">
5 <EditText android:id="@+id/chatbox" android:layout_width="fill_parent"
6 android:layout_height="fill_parent" android:layout_weight="1">
7 </EditText>
8 <EditText android:id="@+id/chattxt" android:layout_width="fill_parent"
9 android:layout_height="wrap_content" android:gravity="top"
10 android:hint="你想和对方说点什么?">
11 </EditText>
12 <Button android:id="@+id/chatOk" android:layout_width="fill_parent"
13 android:layout_height="wrap_content" android:text="Send"
14 android:textSize="@dimen/btn1">
15 </Button>
16
17 </LinearLayout>
2.config.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 android:orientation="vertical"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent">
7 <TextView
8 android:layout_width="fill_parent"
9 android:layout_height="wrap_content"
10 android:text="初始化設置"
11 android:textSize="@dimen/h2"/>
12 <TextView
13 android:layout_width="fill_parent"
14 android:layout_height="wrap_content"
15 android:text="服務器IP"
16 android:textSize="@dimen/h3"/>
17 <EditText
18 android:layout_width="fill_parent"
19 android:layout_height="wrap_content"
20 android:hint="192.168.2.214"
21 android:id="@+id/ip"
22 android:textSize="@dimen/et1"/>
23
24 <TextView
25 android:layout_width="fill_parent"
26 android:layout_height="wrap_content"
27 android:text="端口"
28 android:textSize="@dimen/h3"/>
29 <EditText
30 android:layout_width="fill_parent"
31 android:layout_height="wrap_content"
32 android:hint="8888"
33 android:id="@+id/port"
34 android:textSize="@dimen/et1"/>
35
36 <Button
37 android:layout_width="fill_parent"
38 android:layout_height="wrap_content"
39 android:text="下一步"
40 android:id="@+id/next"
41 android:textSize="@dimen/btn1"/>
42 </LinearLayout>
3.configuer.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <LinearLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 android:orientation="vertical"
5 android:layout_width="match_parent"
6 android:layout_height="match_parent">
7 <TextView
8 android:layout_width="fill_parent"
9 android:layout_height="wrap_content"
10 android:text="初始化設置"
11 android:textSize="@dimen/h2"/>
12 <TextView
13 android:layout_width="fill_parent"
14 android:layout_height="wrap_content"
15 android:text="您的稱呢"
16 android:textSize="@dimen/h3"/>
17 <EditText
18 android:layout_width="fill_parent"
19 android:layout_height="wrap_content"
20 android:hint="潤仔"
21 android:id="@+id/name"
22 android:maxLength="20"
23 android:textSize="@dimen/et1"/>
24
25 <Button
26 android:layout_width="fill_parent"
27 android:layout_height="wrap_content"
28 android:text="完成"
29 android:id="@+id/ok"
30 android:textSize="@dimen/btn1"/>
31 </LinearLayout>
style文件:dimens.xml
1 <?xml version="1.0" encoding="utf-8"?>
2 <resources>
3 <dimen name="h3">30dip</dimen>
4 <dimen name="h2">40dip</dimen>
5 <dimen name="btn1">30dip</dimen>
6 <dimen name="et1">25dip</dimen>
7 </resources>
最后是服务器文件: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.....
发表评论 取消回复