1.先建立两个项目,一个是客户端,一个是服务器端。
项目搭建如下所示:
其中YcChart 为客户端,YcChatServer为服务器端。
2.编写协议类(两边的协议要一样,即自定义的YCCP协议)
package com.yc.entity;
public class YCCP {
public static final int LOGIN = 1; //登录处理
public static final int SEND_MSG = 2; //消息处理
public static final int USER_LIST = 3; //登录用户处理
private int code; // 区分不同的消息
private String content; // 发送的内容
public YCCP() {
}
public YCCP(int code, String content) {
this.code = code;
this.content = content;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
3.Java是面向对象编程,所以我们把一个用户定义为一个User类(两边的User类相同)
package com.yc.entity;
public class User {
private String name; //用户名称
private String ip; //用户IP地址
public User() {
}
public User(String name, String ip) {
this.name = name;
this.ip = ip;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
}
4.编写服务器(Server)
package com.yc.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private ServerSocket server;
public static void main(String[] args) {
new Server().startServer();
}
private void startServer() {
try {
server = new ServerSocket(9090);
System.out.println("服务器启动,,端口9090成功...");
while(true){ //接收多个请求客户端
Socket client = server.accept(); //接受客户请求,没有请求就等待
System.out.println("接收请求: " + client);
new Thread( new ClientStack(client)).start(); //每一个客户请求,都一个线程,这样就不会相互影响了
}
} catch (IOException e) {
System.err.println("服务器启动失败");
}
}
}
5.编写客户端接收处理类(ClientStack)
package com.yc.server;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Scanner;
import com.google.gson.Gson;
import com.yc.entity.User;
import com.yc.entity.YCCP;
public class ClientStack implements Runnable {
private static List<ClientStack> clients = new ArrayList<ClientStack>();
private static List<User> users = new ArrayList<User>();
private Socket client;
private Scanner in;
private PrintWriter out;
private User user;
public ClientStack(Socket client) {
try {
this.client = client;
in = new Scanner(client.getInputStream()); //接收客户端发送过来的数据输入流
out = new PrintWriter( client.getOutputStream()); //向客户端发送响应数据输出流
clients.add(this); //加入当前请求的客户端到集合
} catch (IOException e) {
System.err.println( client.getInetAddress() + ":" + client.getPort() + "断开与服务器连接!!!"); //拼接客户端发送过来的数据
}
}
@Override
public void run() {
boolean isExit= true;
while(isExit){
String line=null;
YCCP yccp=null;
try {
line = in.nextLine(); //拼接客户端发送过来的数据
yccp =new Gson().fromJson(line, YCCP.class);
switch(yccp.getCode()){
case YCCP.LOGIN:
line = doLogin(yccp);
break;
case YCCP.SEND_MSG:
line = acceptMsg(yccp);
break;
}
} catch (Exception e) {
line = user.getName() + "退出了聊天室...\r\n";
clients.remove(this);
users.remove(user);
isExit = false;
}
boardcast(YCCP.SEND_MSG,line);
if(yccp != null && yccp.getCode() == YCCP.LOGIN || !isExit){
boardcast(YCCP.USER_LIST, new Gson().toJson(users));
}
}
}
private void boardcast(int code,String line){
Gson gson = new Gson();
line = gson.toJson(new YCCP(code, line)); //转换成json格式输出
for (ClientStack clientStack:clients) {
clientStack.out.println(line); //给客户端写入的内容
clientStack.out.flush();
}
}
//接收发送消息处理
private String acceptMsg(YCCP yccp) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
String msg = String.format("%s %s : \r\n %s \r\n", user.getName(),sdf.format(new Date()),yccp.getContent());
return msg;
}
//登录处理
private String doLogin(YCCP yccp) {
String uname = yccp.getContent();
String ip = client.getInetAddress() +":" +client.getPort();
user=new User(uname, ip);
users.add(user);
return uname + "加入聊天室... \r\n";
}
}
6.编写客户端ui界面(TalkView)
package com.yc.ui;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Type;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Scanner;
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.custom.SashForm;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.wb.swt.SWTResourceManager;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.yc.entity.User;
import com.yc.entity.YCCP;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
public class TalkView {
protected Shell shell;
private Text uname;
private Text text_1;
private Text text_2;
private Text chatMessage;
private Table table;
private Text message;
private Button connetBtn; // 联接
private Button breakBtn; // 断开
private Button sendBtn; // 发送
private boolean flag = false; //标识变量,控制接收消息处理
private Socket client;
private Scanner in;
private PrintWriter out;
public static void main(String[] args) {
try {
TalkView window = new TalkView();
window.open();
} catch (Exception e) {
e.printStackTrace();
}
}
public void open() {
Display display = Display.getDefault();
createContents();
shell.open();
shell.layout();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
}
protected void createContents() {
shell = new Shell();
shell.setSize(856, 619);
shell.setText("源辰聊天室");
shell.setImage(SWTResourceManager.getImage(TalkView.class, "/images/yc.ico"));
shell.setLayout(new FillLayout(SWT.HORIZONTAL));
SashForm sashForm = new SashForm(shell, SWT.NONE);
sashForm.setOrientation(SWT.VERTICAL);
Group group = new Group(sashForm, SWT.NONE);
group.setText("连接配置");
Label label = new Label(group, SWT.NONE);
label.setBounds(10, 38, 54, 18);
label.setText("昵称:");
uname = new Text(group, SWT.BORDER);
uname.setText("random");
uname.setBounds(78, 38, 70, 18);
Label lblip = new Label(group, SWT.NONE);
lblip.setBounds(177, 38, 54, 18);
lblip.setText("服务器IP:");
text_1 = new Text(group, SWT.BORDER);
text_1.setText("127.0.0.1");
text_1.setBounds(260, 38, 167, 18);
Label label_2 = new Label(group, SWT.NONE);
label_2.setBounds(446, 38, 54, 18);
label_2.setText("端口");
text_2 = new Text(group, SWT.BORDER);
text_2.setText("9090");
text_2.setBounds(521, 38, 70, 18);
connetBtn = new Button(group, SWT.NONE);
connetBtn.setBounds(614, 38, 72, 22);
connetBtn.setText("联接");
breakBtn = new Button(group, SWT.NONE);
breakBtn.setEnabled(false);
breakBtn.setBounds(719, 38, 72, 22);
breakBtn.setText("断开");
Group group_1 = new Group(sashForm, SWT.NONE);
group_1.setText("聊天记录");
group_1.setLayout(new FillLayout(SWT.HORIZONTAL));
SashForm sashForm_1 = new SashForm(group_1, SWT.NONE);
Composite composite = new Composite(sashForm_1, SWT.NONE);
composite.setLayout(new FillLayout(SWT.HORIZONTAL));
chatMessage = new Text(composite, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP
| SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
chatMessage.setBackground(SWTResourceManager.getColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
Group group_3 = new Group(sashForm_1, SWT.NONE);
group_3.setText("在线用户列表");
group_3.setLayout(new FillLayout(SWT.HORIZONTAL));
table = new Table(group_3, SWT.BORDER | SWT.FULL_SELECTION);
table.setHeaderVisible(true);
table.setLinesVisible(true);
TableColumn tableColumn = new TableColumn(table, SWT.NONE);
tableColumn.setWidth(150);
tableColumn.setText("昵称");
TableColumn tblclmnIp = new TableColumn(table, SWT.NONE);
tblclmnIp.setWidth(200);
tblclmnIp.setText("IP");
sashForm_1.setWeights(new int[] { 470, 369 });
Group group_2 = new Group(sashForm, SWT.NONE);
group_2.setText("聊天信息");
Label label_1 = new Label(group_2, SWT.NONE);
label_1.setBounds(36, 29, 54, 18);
label_1.setText("内容");
message = new Text(group_2, SWT.BORDER);
message.setBounds(111, 23, 519, 70);
sendBtn = new Button(group_2, SWT.NONE);
sendBtn.setBounds(700, 51, 72, 22);
sendBtn.setText("发送");
sendBtn.setEnabled(false);
sashForm.setWeights(new int[] { 82, 384, 113 });
doEvent();
}
//事件的集中地
public void doEvent(){
//联接
connetBtn.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
String ipStr=text_1.getText();
String portStr=text_2.getText();
InetAddress address;
try {
address = InetAddress.getByName(ipStr);
} catch (UnknownHostException e1) {
MessageBox mb = new MessageBox(shell);
mb.setText("连接服务器信息");
mb.setMessage("服务连接地址有问题!!!");
mb.open();
return;
}
try {
client = new Socket(address, Integer.parseInt(portStr)); //建立与服务器的连接通道
flag = true;
in = new Scanner(client.getInputStream()); //通道的输入流
out = new PrintWriter(client.getOutputStream()); //通道的输出流
doLogin();
acceptMessage();
breakBtn.setEnabled(true);
connetBtn.setEnabled(false);
} catch (NumberFormatException | IOException e1) {
MessageBox mb = new MessageBox(shell);
mb.setText("连接服务器信息");
mb.setMessage("连接服务器失败,请检查服务器信息");
mb.open();
return;
}
}
});
//断开连接
breakBtn.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
MessageBox mb = new MessageBox(shell,SWT.YES| SWT.NO | SWT.ICON_WARNING);
mb.setText("连接服务器信息");
mb.setMessage("是否断开与服务器的连接?");
if(mb.open()==SWT.YES){
try {
flag = false;
client.close();
chatMessage.setText("");
breakBtn.setEnabled(false);
connetBtn.setEnabled(true);
table.removeAll();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
//发送消息
sendBtn.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
SendMessage();
}
});
//发送信息框处理
message.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent arg0) {
sendBtn.setEnabled("".intern() != message.getText().trim());
}
});
//按Enter键发送
message.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if(e.keyCode==13){
SendMessage();
}
}
});
}
//接收服务器发送过来的消息
public void acceptMessage() {
new Thread(){
public void run() {
while(flag){
if(in.hasNextLine()){
String line = in.nextLine();
YCCP yccp =new Gson().fromJson(line, YCCP.class);
switch(yccp.getCode()){
case YCCP.USER_LIST:
doUserList(yccp);
break;
case YCCP.SEND_MSG:
doMsg(yccp);
break;
}
}
}
};
}.start();
}
public void doMsg(YCCP yccp) {
final String line = yccp.getContent();
Display.getDefault().asyncExec(new Runnable(){
@Override
public void run() {
chatMessage.setText(chatMessage.getText() + "\r\n" + line);
}
});
}
public void doUserList(YCCP yccp) {
Gson gson = new Gson();
Type t=new TypeToken<List<User>>(){}.getType(); //json复杂类型的转换
final List<User> users = gson.fromJson(yccp.getContent(), t);
Display.getDefault().asyncExec(new Runnable(){
@Override
public void run() {
table.removeAll();
for(User user : users){
TableItem ti = new TableItem(table,SWT.NONE);
ti.setText(new String[]{user.getName(),user.getIp()});
}
}
});
}
//向服务器发送消息
public void SendMessage() {
YCCP yccp = new YCCP(YCCP.SEND_MSG,message.getText());
String msg = new Gson().toJson(yccp);
out.println(msg);
out.flush();
message.setText("");
}
public void SendMessage(String msg) {
out.println(msg);
out.flush();
}
//第一次连接做登录
public void doLogin(){
String user = uname.getText();
YCCP yccp = new YCCP(YCCP.LOGIN,user);
SendMessage(new Gson().toJson(yccp));
}
}