昨天下午闲来无事,写了个Android 聊天室的demo 分享给大家
这段代码纯属demo 高手勿喷!
首先是服务端代码:
一个简单的java类,直接运行 设定端口即可
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("端口" + port + "被占用,服务器启动失败!");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
try {
while (started) {
Socket s = ss.accept();
Client c = new Client(s);
System.out.println("一个用户上线");
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 {
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 + " 上线." + date);
}
System.out.println(getnameString + " 上线." + 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 + " 下线." + date);
}
System.out.println(getnameString + " 下线." + 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;
}
}
}
服务端写好了,剩下的就是客户端了。
客户端代码:
首先,不要忘记了权限:
<!-- SD卡读写权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 网络访问权限 -->
<uses-permission android:name="android.permission.INTERNET" />
程序主窗:
public class MainActivity extends Activity {
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 TextView 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 = (TextView) 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) {
System.out.println(e.toString());
}
db.close();
// 设置链接
if (ip == null || port == null) {
Intent intent = new Intent(MainActivity.this, IniActivity.class);
startActivity(intent);
MainActivity.this.finish();
}
// 设置名称
else if (name == null) {
Intent intent = new Intent(MainActivity.this, IniuserActivity.class);
startActivity(intent);
MainActivity.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) {
Toast.makeText(MainActivity.this, "链接超時,服务器未开启或IP错误",
Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this,
IniActivity.class);
startActivity(intent);
MainActivity.this.finish();
e.printStackTrace();
} catch (IOException e) {
Toast.makeText(MainActivity.this, "链接超時,服务器未开启或IP错误",
Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this,
IniActivity.class);
startActivity(intent);
MainActivity.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);
thread = new Thread(null, doThread, "Message");
thread.start();
System.out.println("connect");
isContect = true;
}
} catch (UnknownHostException e) {
Toast.makeText(MainActivity.this, "链接失败", Toast.LENGTH_SHORT)
.show();
Intent intent = new Intent(MainActivity.this, IniActivity.class);
startActivity(intent);
MainActivity.this.finish();
e.printStackTrace();
} catch (SocketTimeoutException e) {
Toast.makeText(MainActivity.this, "链接超時,服务器未开启或IP错误",
Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this, IniActivity.class);
startActivity(intent);
MainActivity.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) {
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) {
e.printStackTrace();
}
}
}
} catch (SocketException e) {
System.out.println("exit!");
} catch (IOException e) {
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() {
super.onDestroy();
disConnect();
// System.exit(0);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, 1, 1, "初始化设置");
menu.add(0, 2, 2, "退出");
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == 1) {
Intent intent = new Intent(MainActivity.this, IniActivity.class);
startActivity(intent);
MainActivity.this.finish();
} else if (item.getItemId() == 2) {
disConnect();
MainActivity.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) {
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) {
}
cursor.close();
db.close();
return result;
}
}
初始化连接窗:
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) {
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) {
System.out.println(e.toString());
}
db.close();
nextButton.setOnClickListener(new nextButtonListenner());
}
class nextButtonListenner implements OnClickListener {
@Override
public void onClick(View v) {
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 {
Toast.makeText(IniActivity.this, getip + ":" + getport,
Toast.LENGTH_SHORT).show();
Toast.makeText(IniActivity.this,
String.valueOf(InetAddress.getLocalHost()),
Toast.LENGTH_SHORT).show();
Socket s = new Socket(getip, Integer.parseInt(getport));
// s.connect(isa, 30000);
// showDialog("連接成功",IniActivity.this);
// 生成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 (UnknownHostException e) {
e.printStackTrace();
showDialog("连接失败!IP或端口不可用!", IniActivity.this);
} catch (SocketTimeoutException e) {
showDialog("连接超时!服务器未开启或IP错误!", IniActivity.this);
e.printStackTrace();
} catch (IOException e) {
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();
}
}
最后一个Activity
public class IniuserActivity extends Activity {
private EditText name;
private Button ok;
private SQLiteDatabase db;
private String nameString;
@Override
protected void onCreate(Bundle savedInstanceState) {
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) {
System.out.println(e.toString());
}
db.close();
}
class okButtonListenner implements OnClickListener {
@Override
public void onClick(View v) {
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,
MainActivity.class);
startActivity(intent);
IniuserActivity.this.finish();
db.close();
} catch (Exception e) {
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();
}
}
还有一个配置信息类:
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"); // 数据库文件
}
蒽. java代码就这么多,剩下的就是布局文件了!
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/chatbox"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:background="#FFFFFF" >
</TextView>
<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" >
</Button>
</LinearLayout>
configuser.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="初始化設置" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="您的昵称" />
<EditText
android:id="@+id/name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLength="20" />
<Button
android:id="@+id/ok"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="完成" />
</LinearLayout>
config.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="初始化設置" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="服務器IP" />
<EditText
android:id="@+id/ip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="请输入IP" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="端口" />
<EditText
android:id="@+id/port"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="请输入端口" />
<Button
android:id="@+id/next"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="下一步" />
</LinearLayout>
到这里一个多人群聊小demo就完成了,
这个小demo里面还是有很多bug的,只能用来练手学习。高手勿喷!