这是一个基于网络线程的可以群聊的SWT工程,支持获取登录的端口号,及用户名,当登录后
用户所发送的信息会经服务器转发给其他所有在线用户,也支持及时显示登录用户,或用户退出。
具体内容:
在bean包下:
1,Content类:
这个类是用来处理客户端发送消息,
存放客户端的ip,用户名,以及要发送的消息。
2,User类:
这个类是用来处理用户登录或退出的,存放了ip地址
和用户名。提供两个构造方法。
3,YcConstants类:
用来存放常量的值,及发送协议所需要的code值。
4,JsonModel类:
存放了一个 code值 和一个泛型,发送协议时用到,
因为这里用到泛型,后面就需要用到反射,也提高了
项目的安全性。
server包:
服务端:TalkServer类
在TalkServer类中写一个ClientSocket内部类并实现Runnable接口。
在ClientSocket类:
创建一个有参的构造方法:用来创建socket,输入流,输出流和通过控制flag的值
来控制线程的运行。
在其run 方法中:
一但有客户端连入flag的值就为true,这段就会变成一个死循环,一直
接受来自客户端的消息,如果读到客户端有下一行消息.
则调用ParseProtocal()方法,进行处理。
在parseProtocal()方法中:
首先会调用parseCode()方法用来判断code的值
在praseCode(String jsonline)方法中,因为从客户端传过来的是一个经gosn解析后的值
形式是{"code:x,...."}.
但是在判断startsWith方法时需要注意应该这么写("{\"code\":x,")
然后返回X;
返回的X=1,则表明t是content对象,则操作文本显示聊天消息
用sendMesgToAllClient(jsonline)方法:
循环list 通过流将内容写出去。List在后面会说明。
返回的X=3则表明有新的用户登录,服务器端处理就是将这个新的登录的用户存到list中,
再将这个用户列表发给各个客户端
a,new 一个Gson 等下用来解析json数据。
b,Type type=new TypeToken<JsonModel<User>>(){}.getType();
注意:因为这里是登录所以这里jsonModel中的放的是User.
这里导包时也应该导入其反射的那个。
JsonModel<User> jsonModel=g.fromJson(jsonline, type);
通过jsonmodel获得值,然再转换
User loginuser=jsonModel.getT();//取出登录用户的信息
List<User> userlist=new ArrayList<User>();
//将新登录的用户加入到list中
ClientSocket.this.ip=loginuser.getIp();
ClientSocket.this.nickname=loginuser.getNickname();
list.add(ClientSocket.this);
然后在循环这个list,将最新的数据添加到userlist,
通过sendUserListJson()方法发送给各个客户端
在这个方法中:
JsonModel<List<User>> jm=new JsonModel<List<User>>();
注意这里jsonmodel里面的泛型是List<User>
将code值设为2,
然后设置jm的反射实列
jm.setT(userlist);
Type t=new TypeToken<JsonModel<User>>(){}.getType();
然后在通过gosn将其转换为json数据
通过循环发个各个对象。
返回的X=4时需要做的就是将list中的这个数据移除,
然后跟上面一样跟新list,将数据发出即可。
start()方法中:
先创建一个服务器端口
然后做个死循环一直监听是否有客户端连上
有客户端连上就会开启线程
在公共区域会定义list用来存 了所有的在线用户列表
调用mian()
就会启动服务器。
client包:
TalkClient类:
登录按钮:
点击登录按钮,获取到界面上的值,
建立Socket,输入,输出流
根据上面的同样的方法将数据转换写出
并开启线程
其他按钮也基本差不多;
但在在刷新消息与联系人列表时值注意:
因为不能直接操作主线程里面的组件;
要用Display.getDefault().asyncExec(new Runnable(){
@Override
public void run() {
..........
......
}
});
将代码套起来。
具体代码:
package com.yc.bean;
//发送的内容
public class Content {
private String ip;
private String nickname;
private String msg;
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return ip+”\t”+nickname+”\t”+msg;
}
}
package com.yc.bean;
public class JsonModel {
private int code;
private T t;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
package com.yc.bean;
//登录退出的用户信息
public class User {
private String nickname;
private String ip;
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public User(String nickname, String ip) {
super();
this.nickname = nickname;
this.ip = ip;
}
public User() {
super();
}
package com.yc.bean;
public class YcConstants {
//登录的code值为3
public static final int USER_LOGIN_CODE=3;
//退出的code值为4
public static final int USER_LOGIN_OUT_CODE=4;
//发送信息的code值为1
public static final int SEND_MESSAGE=1;
//发送用户列表的code值为2
public static final int SEND_USERLIST=2;
package com.yc.server;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Type;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.yc.bean.JsonModel;
import com.yc.bean.User;
import com.yc.bean.YcConstants;
public class TalkServer {
private List list= new ArrayList(); //存 了所有的在线用户列表
public static void main(String[] args) throws IOException{
TalkServer ts=new TalkServer();
ts.start();
}
public void start() throws IOException{
ServerSocket ss=new ServerSocket(23456);
System.out.println("服务器:"+ss.getInetAddress()+"启动,正在监听"+ss.getLocalPort()+"端口");
while(true){
Socket s=ss.accept();
System.out.println("客户端:"+s.getInetAddress()+"连接上了服务器");
//将这个socket放到一个Runnable对像中,用一个线程来操作
ClientSocket cs=new ClientSocket(s);
Thread t=new Thread(cs);
t.start();
}
}
//与一个客户端的套接字
class ClientSocket implements Runnable{
private Socket s;
private Scanner sc;
private PrintWriter pw;
private boolean flag=false;
private String nickname;
private String ip;
//构造方法:创建socket及流,并控制线程的flag 为true;
public ClientSocket(Socket s){
try {
this.s=s;
sc=new Scanner(s.getInputStream());
pw=new PrintWriter(s.getOutputStream());
flag=true;
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端:"+s.getInetAddress()+"已经掉线了");
list.remove(ClientSocket.this);
flag=false;
sc.close();
pw.close();
try {
s.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
@Override
public void run() {
while(flag){
//1接受客户发送过来的数据jsonline
if(sc.hasNextLine()){
String jsonline=sc.nextLine();
parseProtocal(jsonline);
}
}
}
//解析状态码
private int parseCode(String jsonline){
if(jsonline.startsWith("{\"code\":1,")){
return 1;
}else if(jsonline.startsWith("{\"code\":3,")){
return 3;
}else if(jsonline.startsWith("{\"code\":4,")){
return 4;
}
return 404;
}
//发送信息到客户端
private void sendMesgToAllClient(String jsonline){
for(ClientSocket cs:list){
cs.pw.println(jsonline);
cs.pw.flush();
}
}
//解析协议
private void parseProtocal(String jsonline){
//判断codede 值
int code=parseCode(jsonline);
//如果code为1,则表明t是content对象,则操作文本显示聊天消息
if(code==1){
//1是普通聊天信息,直接向所有的客户端广播
sendMesgToAllClient(jsonline);
}else if(code==3){
//如果为3则表明有新的用户登录,服务器端处理就是将这个新的登录的用户存到list中,再将这个用户列表发给各个客户端
Gson g=new Gson();
Type type=new TypeToken<JsonModel<User>>(){}.getType();
JsonModel<User> jsonModel=g.fromJson(jsonline, type);
//将这个user存到list中
User loginuser=jsonModel.getT();//取出登录用户的信息
List<User> userlist=new ArrayList<User>();
//将新登录的用户加入到list中
ClientSocket.this.ip=loginuser.getIp();
ClientSocket.this.nickname=loginuser.getNickname();
list.add(ClientSocket.this);
for(ClientSocket cs: list){
User u=new User();
u.setIp(cs.ip);
u.setNickname(cs.nickname);
userlist.add(u);
}
sendUserListJson(userlist);
}else if(code==4){
//如果是code=4,则要从clientSocket中删除这个用户
/*Gson g=new Gson();
Type type=new TypeToken<JsonModel<User>>(){}.getType();
JsonModel<User> jsonModel=g.fromJson(jsonline, type);*/
//将这个user存到list中
List<User> userlist=new ArrayList<User>();
list.remove(ClientSocket.this);
for(ClientSocket cs: list){
User u=new User();
u.setIp(cs.ip);
u.setNickname(cs.nickname);
userlist.add(u);
}
sendUserListJson(userlist);
}
}
//发送用户列表的json字符串
private void sendUserListJson(List<User> userlist){
String jsonline;
JsonModel<List<User>> jm=new JsonModel<List<User>>();
jm.setCode(YcConstants.SEND_USERLIST);
jm.setT(userlist);
Type t=new TypeToken<JsonModel<User>>(){}.getType();
Gson gson=new Gson();
jsonline=gson.toJson(jm,t);
for(ClientSocket cs:list){
cs.pw.println(jsonline);
cs.pw.flush();
}
}
}
package com.yc.client;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Type;
import java.net.Socket;
import java.net.UnknownHostException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ibm.icu.text.SimpleDateFormat;
import com.yc.bean.Content;
import com.yc.bean.JsonModel;
import com.yc.bean.User;
import com.yc.bean.YcConstants;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
public class TalkClient implements Runnable{
protected Shell shell;
private Text txtForeest;
private Text text_1;
private Text text_2;
private Socket s;
private Scanner sc;
private PrintWriter out;
private Table table;
private Text text_3;
private Text text_4;
private boolean flag;
private String info;
private String nickname;
/**
* Launch the application.
* @param args
*/
public static void main(String[] args) {
try {
TalkClient window = new TalkClient();
window.open();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Open the window.
*/
public void open() {
Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
/**
* Create contents of the window.
*/
protected void createContents() {
shell = new Shell();
shell.setSize(822, 734);
shell.setText("SWT Application");
shell.setLayout(new FillLayout(SWT.HORIZONTAL));
Composite composite = new Composite(shell, SWT.NONE);
composite.setLayout(new FillLayout(SWT.HORIZONTAL));
SashForm sashForm = new SashForm(composite, SWT.NONE);
sashForm.setOrientation(SWT.VERTICAL);
Composite composite_1 = new Composite(sashForm, SWT.NONE);
composite_1.setLayout(new FillLayout(SWT.HORIZONTAL));
Group 登录 = new Group(composite_1, SWT.NONE);
登录.setText("\u767B\u5F55");
登录.setToolTipText("\u767B\u5F55");
Label label = new Label(登录, SWT.NONE);
label.setBounds(20, 29, 44, 17);
label.setText("\u6635\u79F0\uFF1A");
txtForeest = new Text(登录, SWT.BORDER);
txtForeest.setText("foreest");
txtForeest.setBounds(87, 23, 150, 23);
Label label_1 = new Label(登录, SWT.NONE);
label_1.setBounds(278, 29, 48, 17);
label_1.setText("\u670D\u52A1\u5668\uFF1A");
text_1 = new Text(登录, SWT.BORDER);
text_1.setText("localhost");
text_1.setBounds(357, 29, 170, 23);
Label label_2 = new Label(登录, SWT.NONE);
label_2.setBounds(558, 29, 36, 17);
label_2.setText("\u7AEF\u53E3\uFF1A");
text_2 = new Text(登录, SWT.BORDER);
text_2.setText("23456");
text_2.setBounds(614, 29, 144, 23);
final Button button = new Button(登录, SWT.NONE);
button.setBounds(247, 72, 80, 27);
button.setText("\u767B\u5F55");
final Button button_1 = new Button(登录, SWT.NONE);
button_1.setBounds(398, 72, 80, 27);
button_1.setText("\u65AD\u5F00");
Composite composite_2 = new Composite(sashForm, SWT.NONE);
composite_2.setLayout(new FillLayout(SWT.HORIZONTAL));
Group group = new Group(composite_2, SWT.NONE);
group.setText("\u804A\u5929\u8BB0\u5F55");
group.setLayout(new FillLayout(SWT.HORIZONTAL));
SashForm sashForm_1 = new SashForm(group, SWT.NONE);
Composite composite_4 = new Composite(sashForm_1, SWT.NONE);
composite_4.setLayout(new FillLayout(SWT.HORIZONTAL));
text_3 = new Text(composite_4, SWT.BORDER | SWT.READ_ONLY | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL);
Composite composite_5 = new Composite(sashForm_1, SWT.NONE);
composite_5.setLayout(new FillLayout(SWT.HORIZONTAL));
table = new Table(composite_5, SWT.BORDER | SWT.FULL_SELECTION);
table.setHeaderVisible(true);
table.setLinesVisible(true);
TableColumn tableColumn = new TableColumn(table, SWT.NONE);
tableColumn.setWidth(100);
tableColumn.setText("\u6635\u79F0\uFF1A");
TableColumn tblclmnIp = new TableColumn(table, SWT.NONE);
tblclmnIp.setWidth(100);
tblclmnIp.setText("ip");
sashForm_1.setWeights(new int[] {537, 260});
Composite composite_3 = new Composite(sashForm, SWT.NONE);
composite_3.setLayout(new FillLayout(SWT.HORIZONTAL));
Group group_1 = new Group(composite_3, SWT.NONE);
group_1.setText("\u5185\u5BB9");
Label label_3 = new Label(group_1, SWT.NONE);
label_3.setBounds(25, 40, 61, 17);
label_3.setText("\u5185\u5BB9\uFF1A");
text_4 = new Text(group_1, SWT.BORDER);
text_4.setBounds(92, 40, 369, 63);
final Button button_2 = new Button(group_1, SWT.NONE);
button_2.setBounds(537, 106, 80, 27);
button_2.setText("\u53D1\u9001");
sashForm.setWeights(new int[] {150, 324, 151});
//登录
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
nickname=txtForeest.getText().trim().toString();
String ip=text_1.getText().trim().toString();
String port=text_2.getText().trim().toString();
if(nickname==null||"".equals(nickname)){
MessageDialog.openError(shell, "参数错误", "昵称不能为空");
return;
}
if(ip==null||"".equals(ip)){
MessageDialog.openError(shell, "参数错误", "ip不能为空");
return;
}
if(port==null||"".equals(port)){
MessageDialog.openError(shell, "参数错误", "端口不能为空");
return;
}
int p=Integer.parseInt(port);
try {
s=new Socket(ip,p);
sc=new Scanner(s.getInputStream());
out=new PrintWriter(s.getOutputStream());
txtForeest.setEditable(false);
text_1.setEditable(false);
text_2.setEditable(false);
//当服务器连接上了,则启动线程,来监听服务器给我会送信息
button.setEnabled(false);
button_1.setEnabled(true);
button_2.setEnabled(true);
JsonModel<User> jsonModel=new JsonModel<User>();
jsonModel.setCode(YcConstants.USER_LOGIN_CODE);
User user=new User();
user.setNickname(nickname);
user.setIp(s.getLocalAddress().toString());
jsonModel.setT(user);
//通过jsonmodel 对象转为json字符串通过gson转换
//通过TypeToken 对像,向gson来说明这个泛型的构成
Gson g=new Gson();
Type type=new TypeToken<JsonModel<User>>(){}.getType();
String jsonstring=g.toJson(jsonModel, type); //这里有问题:泛型的对象不能被gson解析成字符串
out.println(jsonstring);
out.flush();
flag=true;
Thread t=new Thread(TalkClient.this);
t.start();
} catch (Exception e1) {
e1.printStackTrace();
MessageDialog.openError(shell, "l连接错误", "无法建立连接");
txtForeest.setEditable(true);
text_1.setEditable(true);
text_2.setEditable(true);
button.setEnabled(true);
button_1.setEnabled(false);
button_2.setEnabled(false);
}
}
});
//断开
button_1.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if(s==null){
return;
}
JsonModel<User> jsonModel=new JsonModel<User>();
jsonModel.setCode(YcConstants.USER_LOGIN_OUT_CODE);
User user=new User();
user.setNickname(nickname);
user.setIp(s.getLocalAddress().toString());
jsonModel.setT(user);
Gson g=new Gson();
Type type=new TypeToken<JsonModel<User>>(){}.getType();
String jsonstring=g.toJson(jsonModel, type); //这里有问题:泛型的对象不能被gson解析成字符串
out.println(jsonstring);
out.flush();
//退出
String content=text_3.getText();
Date d=new Date();
SimpleDateFormat sdf=new SimpleDateFormat(
"yyyy年MM月dd日 HH:mm:ss");
content="\n"+nickname+"退出聊天\n\t\t\tt\t"+sdf.format(d)+content;
text_3.setText(content);
out.close();
sc.close();
try {
s.close();
txtForeest.setEditable(true);
text_1.setEditable(true);
text_2.setEditable(true);
button.setEnabled(true);
button_1.setEnabled(false);
button_2.setEnabled(false);
flag=false;
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
//发送
button_2.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
String content=text_4.getText().trim().toString();
if(content==null || "".equals(content)){
MessageDialog.openError(shell, "出错啦,", "要发送的消息不能为空");
return;
}
JsonModel<Content> jsonModel=new JsonModel<Content>();
jsonModel.setCode(YcConstants.SEND_MESSAGE);
Content c=new Content();
c.setIp(s.getInetAddress().toString());
c.setNickname(nickname);
c.setMsg(content);
jsonModel.setT(c);
//将jsonModel对象转换为json字符串通过gson转换
Type type=new TypeToken<JsonModel<Content>>(){}.getType();
Gson gson=new Gson();
String jsonstring =gson.toJson(jsonModel,type);
out.println(jsonstring);
out.flush();
text_4.setText("");
}
});
}
@Override
public void run() {
//死循环的接受服务器端传过来的信息
while(flag){
//接受服务器的回传数据
if(s!=null && s.isClosed()==false && sc!=null && sc.hasNextLine()){
String jsonline=sc.nextLine();
//解析协议
parseProtocal(jsonline);
}
}
}
private void parseProtocal(String jsonline){
//判断code的值
int code=parseCode(jsonline);
//如果code为1则表明 t是content对象,则操作文本显示聊天消息
if(code==1){
Gson g=new Gson();
Type type=new TypeToken<JsonModel<Content>>(){}.getType();
JsonModel<Content> jsonModel=g.fromJson(jsonline, type);
final Content c=jsonModel.getT();
//在swt中不能直接在新的线程中操作主线程中的组件,只能通过异步的方式
Display.getDefault().asyncExec(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
Date d=new Date();
SimpleDateFormat sdf=new SimpleDateFormat(
"yyyy年MM月dd日 HH:mm:ss");
String txt=text_3.getText().trim();
String msg="\n\n"+c.getNickname()+"对大家说:\n"+c.getMsg()+"\n\t\t\t\t\t发表时间:"+sdf.format(d)+"\n";
txt=msg+txt;
text_3.setText(txt);
}
});
}else if(code==2){
//如果为2则表t是Userlist集合对象,则更新table
// System.out.println("2222222222222");
Gson g=new Gson();
Type type=new TypeToken<JsonModel<List<User>>>(){}.getType();
JsonModel<List<User>> jsonModel=g.fromJson(jsonline, type);
final List<User> list=jsonModel.getT();
Display.getDefault().asyncExec(new Runnable(){
@Override
public void run() {
table.removeAll();
for(User u:list){
TableItem ti=new TableItem(table,SWT.NONE);
ti.setText(new String[]{u.getNickname(),u.getIp()});
}
}
});
}
}
//解析状态码
private int parseCode(String jsonline){
if(jsonline.startsWith("{\"code\":1,")){
return 1;
}else if(jsonline.startsWith("{\"code\":2,")){
return 2;
}
return 404;
}
}