QQProtocal.java
package com.InetAddress;
public interface QQProtocal {
String UserName = "§я";
}
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.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());
}
//读到的是普通的聊天信息
else{
//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(line);//打印到控制台
ps.println(line);//输出到各客户端的socket,就相当于打印到客户端控制台上
}catch(SocketException sex){
//此时捕获到的异常即是客户端断开连接时会产生的SocketException,要把这个客户端从set集合中删除
//此处可以不做任何处理,只要在最后捕获SocketException就可以了
FirstServer.clients.remove(s);
//sex.printStackTrace();
}catch(Exception ex){
ex.printStackTrace();
}
}//for
}
}//while
}catch(SocketException sex){
//把退出的客户端的socket从集合中删除
//因为此处是以socket来删除,所以Map里就用socket作为键值对的键
FirstServer.clients.remove(socket);
}catch(Exception ex){
ex.printStackTrace();
}
}//run()
}//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;
}
//把用户名发送给服务器,用户名前后各加两个特殊的字符,用以判断是用户名,而不是普通消息
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) {
//获得用户输入的内容
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){
//打印到控制台
System.out.println(keyline1);
//将聊天信息在文本域中显示
display.append(keyline1+"\n");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}