QQProtocal.java
package com.InetAddress;
public interface QQProtocal {
String UserName = "§я";
String UserList = "ζω";
}
FirstServer.java
package com.InetAddress;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class FirstServer {
//线程安全Set集合
public static Map<Socket,String> clients = Collections.synchronizedMap(
new HashMap<Socket,String>());
public static void main(String[] args) throws IOException {
//ServerSocket只负责接收连接
ServerSocket ss = new ServerSocket(9999);
System.out.println("服务器正在等待连接:");
while(true){
//Socket会阻塞线程
Socket s = ss.accept();
new ServerThread(s).start();
}
}
}
class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket){
this.socket = socket;
}
public void run(){
/*
* 从socket里面读,然后打印到控制台
*/
try{
//使用BufferedReader的好处是可以一次读一行
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while((line = br.readLine())!=null){
//读到的是用户名,外面包着自定义的协议。
if(line.startsWith(QQProtocal.UserName) && line.endsWith(QQProtocal.UserName)){
//把读取的包装好的用户名拆分出真正的用户名
String UserName = line.substring(QQProtocal.UserName.length(),
line.length()-QQProtocal.UserName.length());
// System.out.println(line.length());
// System.out.println(QQProtocal.UserName.length());
// System.out.println(line.length()-QQProtocal.UserName.length());
System.out.println(UserName);
//每当有一个客户端连接就把该客户端的socket加入到Map集合中
FirstServer.clients.put(socket,UserName);
System.out.print("已连接");
System.out.print(FirstServer.clients.values());
System.out.println("客户端数量:"+FirstServer.clients.size());
//发送所有客户端的用户名
BroadCast(QQProtocal.UserList+FirstServer.clients.values()+QQProtocal.UserList);
}
//读到的是普通的聊天信息
else{
BroadCast(line);
}
}//while
}catch(SocketException sex){
//把退出的客户端的socket从集合中删除
//因为此处是以socket来删除,所以Map里就用socket作为键值对的键
FirstServer.clients.remove(socket);
BroadCast(QQProtocal.UserList+FirstServer.clients.values()+QQProtocal.UserList);
}catch(Exception ex){
ex.printStackTrace();
}
}//run()
//服务器端每次修改了客户端(remove())之后,要把信息返回给客户端
//用来广播消息
private void BroadCast(String info){
//for(Socket s : FirstServer.clients){
//用迭代器来遍历HashMap集合,clients.keySet()取到Map里的键后再迭代
for(Iterator<Socket> it = FirstServer.clients.keySet().iterator();it.hasNext(); ){
Socket s = it.next();
try{
//往socket里面写,println()一次就会清空一次
PrintStream ps = new PrintStream(s.getOutputStream());
System.out.println(info);//打印到控制台
ps.println(info);//输出到各客户端的socket,就相当于打印到客户端控制台上
}catch(SocketException sex){
//此时捕获到的异常即是客户端断开连接时会产生的SocketException,要把这个客户端从set集合中删除
//此处可以不做任何处理,只要在最后捕获SocketException就可以了
FirstServer.clients.remove(s);
//sex.printStackTrace();
}catch(Exception ex){
ex.printStackTrace();
}
}//for
}
}//Thread
FirstClient.java
package com.InetAddress;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class FirstClient {
private JFrame mainwin = new JFrame("聊天窗口");
private JTextArea display = new JTextArea(15,40);
private JTextField input = new JTextField(40);
JButton send = new JButton("发送");
//DefaultListModel用来在用户列表中添加用户
private DefaultListModel listmodel = new DefaultListModel();
private JList list = new JList(listmodel);//用户列表
private JCheckBox isprivate = new JCheckBox("私聊");
public void init() throws UnknownHostException, IOException{
Socket s = new Socket("127.0.0.1",9999);//创建连接
final PrintStream socketOut = new PrintStream(s.getOutputStream());
//获取用户输入的用户名
String tip = "";
while(true){
String UserName = JOptionPane.showInputDialog(mainwin, tip+"请您输入用户名",
"输入用户名",JOptionPane.QUESTION_MESSAGE);
if(UserName == null || UserName.trim().equals("")){
tip = "您没有填写用户名,";
continue;
}
//把用户名发送给服务器,用户名前后各加两个特殊的字符,用以判断是用户名,而不是普通消息
//往socket里写
socketOut.println(QQProtocal.UserName + UserName + QQProtocal.UserName);
// System.out.println(QQProtocal.UserName + UserName + QQProtocal.UserName);
break;
}
display.setEditable(false);//设置永不不允许编辑文本域部分
mainwin.add(new JScrollPane(display));
JPanel bottompanel = new JPanel();//默认流式布局
bottompanel.add(input);//加滚动条
bottompanel.add(send);
mainwin.add(bottompanel,BorderLayout.SOUTH);
JPanel rightPanel = new JPanel();
rightPanel.setLayout(new BorderLayout());
list.setFixedCellWidth(60);
rightPanel.add(new JScrollPane(list));//加滚动条
rightPanel.add(isprivate,BorderLayout.SOUTH);
mainwin.add(rightPanel,BorderLayout.EAST);
mainwin.pack();
mainwin.setVisible(true);
mainwin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
new ClientThread(s).start();
//局部变量在内部类中访问要加final
//给send按钮加监听器
send.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
//获得用户输入的内容,trim()去掉前后空格
String inputContent = input.getText().trim();
if(inputContent.length()>0){
//把input中的内容发送到socket里
socketOut.println(inputContent);
//发送信息发送完以后应该清空文本框
input.setText("");
}
}
} );//send
//给input按钮加监听器
input.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
//获得用户输入的内容
String inputContent = input.getText().trim();
if(inputContent.length()>0){
//把input中的内容发送到socket里
socketOut.println(inputContent);
//发送信息发送完以后应该清空文本框
input.setText("");
}
}
} );//send
}//init()
public static void main (String[] args) throws IOException {
new FirstClient().init();
}
//作为内部类,可以访问私有的成员变量
class ClientThread extends Thread{
private Socket socket;
public ClientThread(Socket socket){
this.socket = socket;
}
public void run(){
try {
/*
*从socket读,打印到控制台
*/
//把字节流转化成字符流,从socket读
InputStreamReader reader1 = new InputStreamReader(socket.getInputStream());
//使用BufferedReader的好处是可以一次读一行
BufferedReader br1 = new BufferedReader(reader1);
//往socket里面写,println()一次就会清空一次
//PrintStream ps = new PrintStream(s.getOutputStream());
String keyline1 = null;
while((keyline1 = br1.readLine())!=null){
//判断发送过来的消息的种类,此处是服务器发送过来有哪些客户端在线的消息
if(keyline1.startsWith(QQProtocal.UserList) &&
keyline1.endsWith(QQProtocal.UserList)){
//UserList是一个HashMap的value值,形式如[aaa,bbb],其中,aaa,bbb分别为客户端的用户名
//所以此处得到子串(出去协议的头和尾)的时候要去掉[],所以都取长度-1
//把读取的包装好的用户名拆分出真正的用户名
String UserList = keyline1.substring(QQProtocal.UserList.length()+1,
keyline1.length()-QQProtocal.UserList.length()-1);
System.out.println(UserList);
//用,来分解多个用户名
String[] usernames = UserList.split(",");
listmodel.clear();
for(int i =0;i<usernames.length;++i){
//添加用户名到用户列表
listmodel.add(i, usernames[i].trim());
}
// display.append(UserList+"\n");
}
//打印到控制台
// System.out.println(keyline1);
//将聊天信息在文本域中显示
else{
display.append(keyline1+"\n");
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}