v 項目源碼
https://github.com/hjzgg/java_QQ
v 標題效果

package testFour; import java.awt.Color; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.BindException; import java.net.ServerSocket; import java.util.Iterator; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextPane; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; public class QQ { public static final int PICUTER = 1; public static final int FILE = 2; public static final int PARAGRAPH = 3; public static final int SEND = 1; public static final int RECEIVE = 2; public static final int FILEX = 3; public static int getUnusedPort() throws BindException{ int port; for(port = 10000 ; port <= 65535; ++port){ ServerSocket ss = null; try { ss = new ServerSocket(port); } catch (IOException e) { e.printStackTrace(); } if(ss != null) break; } if(port > 65535) throw new BindException("沒有可用的端口!"); return port; } public synchronized static void setTextPane(JTextPane tp, byte[] bt, int len, int flag, int tag){ ImageIcon myIcon = null; if(tag == SEND) myIcon = new ImageIcon("ff.jpg"); else if(tag == RECEIVE) myIcon = new ImageIcon("gg.jpg"); else if(tag == FILEX) myIcon = new ImageIcon("kk.jpg"); SimpleAttributeSet set = new SimpleAttributeSet(); Document doc = tp.getStyledDocument(); if(tag == SEND || tag == RECEIVE){ tp.setCaretPosition(doc.getLength()); JLabel fileLabel = new JLabel("", myIcon, JLabel.CENTER); tp.insertComponent(fileLabel); } if(flag == PARAGRAPH){ FontMetrics fm = tp.getFontMetrics(tp.getFont()); int paneWidth = tp.getWidth(); String text = new String(bt, 0, len); if( tag/3 == SEND ){ StyleConstants.setForeground(set, new Color(0,255,0));//設置文字顏色 StyleConstants.setFontSize(set, 15);//設置字體大小 } else if( tag/3 == RECEIVE){ StyleConstants.setForeground(set, new Color(0,0,0));//設置文字顏色 StyleConstants.setFontSize(set, 15);//設置字體大小 } try { for(int i = 0, cnt = 0; i < text.length(); ++i){ if((cnt += fm.charWidth(text.charAt(i))) >= paneWidth){ cnt = 0; doc.insertString(doc.getLength(), "\n", set); continue; } doc.insertString(doc.getLength(), String.valueOf(text.charAt(i)), set); } doc.insertString(doc.getLength(), "\n", set); tp.setCaretPosition(doc.getLength());//最簡單的設置滾動條的位置到最后輸出文本的地方 //就是將JTextPane中的插入符的位置移動到文本的末端! } catch (BadLocationException e) { } } else if(flag == PICUTER) { try{ InputStream is = new ByteArrayInputStream(bt, 0, len); ImageInputStream iis = ImageIO.createImageInputStream(is); Iterator<ImageReader> itImage = ImageIO.getImageReaders(iis); if(itImage.hasNext()){ ImageReader reader = itImage.next(); ImageIcon ii = new ImageIcon( bt, reader.getFormatName() ); tp.setCaretPosition( doc.getLength() ); tp.insertComponent( new PicuterPanel(ii) ); doc.insertString(doc.getLength(), "\n", set); } }catch(IOException ex){ ex.printStackTrace(); }catch(BadLocationException e1){ } } else if(flag == FILE) { try{ tp.setCaretPosition(doc.getLength()); JLabel fileLabel = new JLabel(new String(bt, 0, len), myIcon, JLabel.LEFT); tp.insertComponent(fileLabel); doc.insertString(doc.getLength(), "\n", set); }catch (BadLocationException e) { e.printStackTrace(); } } } } class PicuterPanel extends JPanel{ private ImageIcon ii; public PicuterPanel(ImageIcon ii){ this.ii = ii; setPreferredSize(new Dimension(200, 300)); setBackground(new Color(255, 255, 255)); } protected void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(ii.getImage(), 0, 0, 200, 300, 0, 0, ii.getIconWidth(), ii.getIconHeight(), this); } }

package testFour; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dialog; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Frame; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.ScrollPaneConstants; public class QQDialog extends Frame{ private JPanel QQP = new JPanel(); private JButton myFriend = new JButton("QQ好友>>"); private JPanel funP = new JPanel(); private JButton add = new JButton("添加好友"); private JButton serach = new JButton("查詢好友"); private JPanel pp = new JPanel(); private JPanel tmp = null; private final int QQPheight = 397; private JScrollPane jsp = new JScrollPane(QQP); private static Set<String>QQset;//IP的集合 private static Map<String, QQFrame>QQmap;//IP到對通信窗口的映射 private static Map<String, String>nameToIP;//姓名到IP的映射 ImageIcon[] ii = new ImageIcon[3]; public static Map<String, QQFrame> getMap(){ return QQmap; } public static Set<String> getSet(){ return QQset; } public static Map<String, String> getNameToIP(){ return nameToIP; } public QQDialog(){ QQset = new TreeSet<String>(); QQmap = new TreeMap<String, QQFrame>(); nameToIP = new TreeMap<String, String>(); ii[0] = new ImageIcon("ff.jpg"); ii[1] = new ImageIcon("gg.jpg"); ii[2] = new ImageIcon("kk.jpg"); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); jsp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); jsp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); pp.add(jsp); pp.setLayout(new BoxLayout(pp, BoxLayout.Y_AXIS)); QQP.setLayout(new BoxLayout(QQP, BoxLayout.Y_AXIS)); JPanel pmyFriend = new JPanel(); pmyFriend.setLayout(new BorderLayout()); pmyFriend.add(myFriend, "Center"); pmyFriend.setPreferredSize(new Dimension(300, 20)); add(pmyFriend); pp.setPreferredSize(new Dimension(300, 400)); add(pp); add(funP); JLabel myself = null; try { myself = new JLabel(InetAddress.getLocalHost().getHostName() + ":" + InetAddress.getLocalHost().getHostAddress()); } catch (UnknownHostException e) { e.printStackTrace(); } addLabelListener(myself); myself.setIcon(new ImageIcon("ff.jpg")); tmp = new JPanel(); tmp.setLayout(new FlowLayout(FlowLayout.CENTER)); tmp.setBackground(new Color(255, 0, 255)); tmp.setPreferredSize(new Dimension(250, 60)); tmp.add(myself); QQP.add(tmp); funP.setLayout(new FlowLayout(FlowLayout.CENTER)); funP.add(add); funP.add(serach); myFriend.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) {//將好友面板進行收縮的效果! if("QQ好友>>".equals(myFriend.getText())){ QQP.setVisible(false); myFriend.setText("QQ好友<<"); } else{ QQP.setVisible(true); myFriend.setText("QQ好友>>"); } } }); add.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { InputDialog dlg = new InputDialog(); if(dlg.key == InputDialog.CANCELBTN || dlg.key== 0) return; Random rd = new Random(); int index = Math.abs(rd.nextInt()) % 3; JLabel ll = new JLabel(dlg.getNameText() + ":" + dlg.getIPText(), ii[index], JLabel.LEFT); QQset.add(dlg.getIPText());//將新增加的好友添加到集合中! nameToIP.put(dlg.getNameText(), dlg.getIPText()); tmp = new JPanel(); tmp.setLayout(new FlowLayout(FlowLayout.CENTER)); tmp.setBackground(new Color(255, 0, 255)); /* * BoxLayout布局簡直是蛋疼的要命,一個面板X是BoxLayout布局,如果該面板添加一個面板Y的話 * 那么Y就會填充X面板!如果在添加一個面板Z, 那么Y, Z就會一起布滿X面板!但是可以設置Y,Z面板 * 的比例! 如果X添加的是一個按鈕或者標簽時,還不能控制其大小.....無語了! * * 下面的我的做法將標簽添加到面板tmp中,然后再將tmp添加中QQP面板中!這樣就可以控制標簽的大小了! * 再添加新的面板的時候,要設置一下之前面板的PreferredSize!保證每一個標簽的距離適中! * 也就是保證所有的添加的面板的高度之和 == QQP.getHeight(); * */ int cnt = QQP.getComponentCount();//顯示QQ好友的個數! if(cnt >= 1) QQP.getComponent(cnt-1).setPreferredSize(new Dimension(250, 60)); int h = QQP.getHeight() - cnt*60; if(h < 0) h=60; tmp.setPreferredSize(new Dimension(250, h)); tmp.add(ll); QQP.add(tmp); QQP.add(tmp); addLabelListener(ll);//給標簽添加監聽器 JScrollBar jsb = jsp.getVerticalScrollBar(); QQP.updateUI();//利用當前外觀的值重置 UI 屬性。 也可以保證滾動條隨時的更新 //終於搞好了,將垂直滾動條自動的移動到最低端 jsp.getViewport().setViewPosition(new Point(0, jsp.getVerticalScrollBar().getMaximum())); } }); serach.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { String name = JOptionPane.showInputDialog(null, "好友姓名", "好友查詢", JOptionPane.OK_OPTION); if(name == null) return ; String ip = nameToIP.get(name); if(ip == null) JOptionPane.showMessageDialog(null, "好友不存在!", "查詢結果", JOptionPane.OK_OPTION); else{ QQFrame fm = QQmap.get(ip); if(fm == null){ try{ String[] ipStr = ip.split("\\.");//將字符串中的數字分離 byte[] ipBuf = new byte[4];//存儲IP的byte數組 for(int i = 0; i < 4; i++){ ipBuf[i] = (byte)(Integer.parseInt(ipStr[i])&0xff); } fm = new QQFrame(name, ipBuf); }catch(RuntimeException ex){ JOptionPane.showMessageDialog(null, ex.getMessage(), "Socket錯誤!", JOptionPane.OK_CANCEL_OPTION); return ; } QQmap.put(ip, fm); } else fm.requestFocus(); } } }); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { if(qr == null) System.out.println("hehe"); qr.setFlag();//udp服務線程停止 ss.setFlag();//tcp服務線程停止 System.exit(0); } }); QQP.setBackground(new Color(255, 0, 255)); funP.setBackground(new Color(255, 255, 0)); setResizable(false); setSize(300, 500); setLocation(500, 200); setVisible(true); } public void addLabelListener(final JLabel lab){ lab.setOpaque(true); lab.setBackground(new Color(255, 255, 255)); lab.setPreferredSize(new Dimension(250, 55)); String NameAndIP = lab.getText(); String name = NameAndIP.substring(0, NameAndIP.indexOf(':')); String ip = NameAndIP.substring(NameAndIP.indexOf(':') + 1, NameAndIP.length()); nameToIP.put(name, ip); QQset.add(ip); lab.addMouseListener(new MouseAdapter() { Color oldC = lab.getBackground(); public void mouseEntered(MouseEvent e) { lab.setBackground(new Color(0, 255, 0)); } public void mouseExited(MouseEvent e) { lab.setBackground(oldC); } public void mouseClicked(MouseEvent e) { //打開通信窗口 if(e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2){ String NameAndIP = lab.getText(); String name = NameAndIP.substring(0, NameAndIP.indexOf(':')); if(QQmap.get(nameToIP.get(name)) != null){//這個好友的窗口已經存在! QQmap.get(nameToIP.get(name)).requestFocus(); return ; } QQFrame fm = null; try{ String[] ipStr = nameToIP.get(name).split("\\.");//將字符串中的數字分離 byte[] ipBuf = new byte[4];//存儲IP的byte數組 for(int i = 0; i < 4; i++){ ipBuf[i] = (byte)(Integer.parseInt(ipStr[i])&0xff); } fm = new QQFrame(name, ipBuf); QQmap.put(nameToIP.get(name), fm);//name 和 窗口的映射! }catch(RuntimeException ex){ JOptionPane.showMessageDialog(null, ex.getMessage(), "錯誤提示!", JOptionPane.OK_CANCEL_OPTION); return ; } } else if(e.getButton() == MouseEvent.BUTTON3){ JPopupMenu pm = new JPopupMenu("胡峻崢"); JMenuItem del = new JMenuItem("刪除"); del.setFont(new Font("華文行楷", Font.ITALIC, 20)); del.setForeground(new Color(255, 0, 0)); del.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int choose = JOptionPane.showConfirmDialog(null, "確定刪除!", "刪除對話框", JOptionPane.YES_NO_OPTION); if(choose == JOptionPane.OK_OPTION){ QQP.remove(lab.getParent()); int cnt = QQP.getComponentCount(); int h = QQP.getHeight() - (cnt-1)*60; if(h < 0) h=60; if(cnt >= 1) QQP.getComponent(cnt-1).setPreferredSize(new Dimension(250, h)); cnt = QQP.getComponentCount(); if(cnt*60 <= QQPheight) QQP.setPreferredSize(new Dimension(QQP.getWidth(), QQPheight)); else QQP.setPreferredSize(new Dimension(QQP.getWidth(), cnt*60)); QQP.updateUI(); String NameAndIP = lab.getText(); String ip = NameAndIP.substring(NameAndIP.indexOf(':') + 1); QQset.remove(ip); QQFrame fm = QQmap.get(ip); if(fm != null) fm.dispose(); nameToIP.remove(NameAndIP.substring(0, NameAndIP.indexOf(':'))); QQmap.remove(ip); } } }); JMenuItem edit = new JMenuItem("編輯"); edit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { //得到之前的數據! String content = lab.getText(); String oldName = content.substring(0, content.indexOf(':')); String oldIP = content.substring(content.indexOf(':')+1); //從新設置到文本框中 InputDialog id = new InputDialog(oldIP, oldName, true); if(id.key == InputDialog.CANCELBTN || id.key== 0) return; lab.setText(id.NAME + ":" + id.IP); QQFrame fm = QQmap.get(oldIP); if(fm != null){ try{ fm.setSendIAD(id.IP); QQmap.put(id.IP, fm); fm.setTitle(id.NAME); QQmap.remove(oldIP); QQset.remove(oldIP); QQset.add(id.IP); nameToIP.remove(oldName); nameToIP.put(id.NAME, id.IP); }catch(RuntimeException ex){ JOptionPane.showMessageDialog(null, ex.getMessage(), "修改之后的IP!", JOptionPane.OK_OPTION); } } } }); edit.setFont(new Font("華文行楷", Font.ITALIC, 20)); edit.setForeground(new Color(255, 0, 255)); pm.setBorderPainted(true); pm.setBackground(new Color(125, 0, 125)); pm.add(del); pm.add(edit); pm.show(lab, e.getX(), e.getY()); } } }); } //內部類,用來訪問Frame中的數據成員 class InputDialog extends Dialog{ private JLabel ipLabel = new JLabel("IP地址:"); private JLabel nameLabel = new JLabel("姓名:"); private JButton okBtn = new JButton("確定"); private JButton cleBtn = new JButton("取消"); private JTextField ipText = new JTextField(20); private JTextField nameText = new JTextField(20); private static final int OKBTN = 1; private static final int CANCELBTN = 2; private int key = 0; private String IP = null, NAME = null; private boolean flag = false; void initDialog(){ JPanel p = null; setModal(true); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); add(p = new JPanel()); p.setLayout(new FlowLayout(FlowLayout.CENTER)); ipLabel.setPreferredSize(new Dimension(50, 12)); p.add(ipLabel); p.add(ipText); add(p = new JPanel()); p.setLayout(new FlowLayout(FlowLayout.CENTER)); nameLabel.setPreferredSize(new Dimension(50, 12)); p.add(nameLabel); p.add(nameText); add(p = new JPanel()); p.setLayout(new FlowLayout(FlowLayout.CENTER)); p.add(okBtn); p.add(cleBtn); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { key = 0; dispose(); } }); addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { requestFocus(); } }); okBtn.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { key = InputDialog.OKBTN; if("".equals(ipText.getText()) || "".equals(nameText.getText()) || !checkIP(ipText.getText()) ) JOptionPane.showMessageDialog(null, "信息不全或者IP填寫錯誤!"); else{ String ip = ipText.getText(); if(!flag && QQset.contains(ip)){ JOptionPane.showMessageDialog(null, "好友已存在!", "QQ好友", JOptionPane.OK_OPTION); return ; } IP = ipText.getText(); NAME = nameText.getText(); dispose(); } } }); cleBtn.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { key = InputDialog.CANCELBTN; dispose(); } }); setSize(300, 200); setLocation(200, 200); setVisible(true); } public InputDialog(String ip, String name){ super(new Frame()); ipText.setText(ip); nameText.setText(name); initDialog(); } public InputDialog(String ip, String name, boolean flag){ super(new Frame()); ipText.setText(ip); nameText.setText(name); this.flag = flag; initDialog(); } public InputDialog(){ super(new Frame()); initDialog(); } public boolean checkIP(String ip){ int i, begin = 0, end; for(i = 1; i <= 4; ++i){ end = ip.indexOf('.', begin); if(end == -1) return false; int p = Integer.valueOf(ip.substring(begin, end)); if(p < 0 || p > 255) return false; } return true; } public String getIPText(){ return IP; } public String getNameText(){ return NAME; } } private static ServerSocketQQ ss = null; private static QQReceive qr = null; public static void main(String[] args){ ss = new ServerSocketQQ(); new Thread(ss).start(); if(ServerSocketQQ.getPort() < 1) return; qr = new QQReceive(); new Thread(qr).start(); if(QQReceive.getPort() < 1) return ; new QQDialog(); } }

package testFour; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Frame; import java.awt.Label; import java.awt.TextArea; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.UnknownHostException; import java.util.Calendar; import java.util.Iterator; import java.util.Map; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextPane; import javax.swing.filechooser.FileFilter; public class QQFrame extends Frame{ private TextArea taSend = new TextArea(); private JTextPane taReceive = new JTextPane(); private JScrollPane p = new JScrollPane(taReceive); private JPanel pSend = new JPanel(); private JPanel pReceive = new JPanel(); private Label laSend = new Label("發送端....."); private Label laReceive = new Label("接收端....."); private JButton FileBtn = new JButton("傳輸文件"); private JButton PicuterBtn = new JButton("發送圖片"); private InetAddress sendIAD = null;//當前窗口所對應的好友所在機器的IP對象 private String QQname = null;//該對話框所對應的好友的姓名 private Socket st =null; private String text; private DatagramSocket ds = null; public QQFrame(String name, byte[] ipBuf) throws RuntimeException{ try { sendIAD = InetAddress.getByAddress(ipBuf); } catch (UnknownHostException e3) { throw new RuntimeException("IP錯誤(不存在或無發鏈接)!"); } ds = QQReceive.getUdpSocket(); if(ds == null) throw new RuntimeException("udp Socket出錯!"); QQname = name; text = ""; setSize(600, 600); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); pSend.setLayout(new FlowLayout(FlowLayout.LEFT)); pSend.add(laSend); pSend.add(FileBtn); pSend.add(PicuterBtn); pReceive.setLayout(new FlowLayout(FlowLayout.LEFT)); pReceive.add(laReceive); taReceive.setForeground(new Color(255, 0, 255)); add(pReceive); add(p); add(pSend); add(taSend); setTitle("我的好友 " + QQname); taSend.setPreferredSize(new Dimension(0, 200)); taReceive.setPreferredSize(new Dimension(0, 400)); taSend.setFont(new Font("仿宋", Font.PLAIN, 20)); taReceive.setFont(new Font("黑體", Font.PLAIN, 25)); taReceive.setEditable(false);//不能進行文本的編輯,但是可以進行訪問 taSend.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if(e.getKeyCode() == KeyEvent.VK_ENTER){ text = taSend.getText(); if(text == null) return; text += "\n\n"; byte[] bt = text.getBytes(); DatagramPacket dp = null; try { //向指定的ip和端口發送數據~! //先說明一下數據是誰發送過來的! byte[] ip = InetAddress.getLocalHost().getHostAddress().getBytes(); dp = new DatagramPacket(ip, ip.length, sendIAD, QQReceive.getPort()); ds.send(dp); //這里主要是因為多可數據報包發送時會產生丟包的情況...所以暫停一段時間! try { Thread.sleep(100); } catch (InterruptedException e1) { } dp = new DatagramPacket("PARAGRAPH".getBytes(), "PARAGRAPH".getBytes().length, sendIAD, QQReceive.getPort()); ds.send(dp); try { Thread.sleep(100); } catch (InterruptedException e1) { } dp = new DatagramPacket(bt, bt.length, sendIAD, QQReceive.getPort()); ds.send(dp); } catch (IOException e1) { e1.printStackTrace(); } synchronized(QQ.class){//發送端向接收窗口添加數據時 要 和 接收端向接收窗口添加數據時同步! byte[] x = null; try { x = new String(InetAddress.getLocalHost().getHostName() + " : ").getBytes(); } catch (UnknownHostException e1) { e1.printStackTrace(); } QQ.setTextPane(taReceive, x, x.length, QQ.PARAGRAPH, QQ.SEND); x = text.getBytes(); QQ.setTextPane(taReceive, x, x.length, QQ.PARAGRAPH, QQ.SEND*3); taSend.requestFocus(); } taSend.setText("");//發送端清空 e.consume();//不讓這個回車字符在輸入端顯示! return ; } } }); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { Map<String, QQFrame>mp = QQDialog.getMap(); mp.remove(sendIAD.getHostAddress()); dispose(); } }); FileBtn.addMouseListener(new MouseAdapter() {//文件傳輸 public void mouseClicked(MouseEvent e) { JFileChooser jfc = new JFileChooser(); jfc.showOpenDialog(null); File fl = jfc.getSelectedFile(); if(fl == null) return ; try { st = new Socket();//嘗試連接對方 st.connect(new InetSocketAddress(sendIAD, ServerSocketQQ.getPort()), 1000); } catch (IOException e2) { st = null; JOptionPane.showMessageDialog(null, "請求超時或連接錯誤!", "ServerSocket", JOptionPane.OK_CANCEL_OPTION); } if(st != null){ try { byte[] bt = new byte[1024]; InputStream is = st.getInputStream(); OutputStream os = st.getOutputStream(); //先說明一下是誰發送過來的! byte[] ip = InetAddress.getLocalHost().getHostAddress().getBytes(); os.write(ip); os.flush(); try { Thread.sleep(100); } catch (InterruptedException e1) { } //向對方首先發送文件名, 然后發送文件內容! os.write(fl.getName().getBytes()); os.flush(); try { Thread.sleep(100); } catch (InterruptedException e1) { } int len; InputStream fis = new FileInputStream(fl); while( (len = fis.read(bt)) != -1){ os.write(bt, 0, len); os.flush(); } bt = new String(Calendar.getInstance().getTime().toString() + ":文件已傳輸!").getBytes(); QQ.setTextPane(taReceive, bt, bt.length, QQ.FILE, QQ.FILEX); st.shutdownOutput();//輸出流結束,並標記一下,使服務端知道客戶端輸出已經結束了! st.close(); } catch (IOException e1) { e1.printStackTrace(); } } } }); PicuterBtn.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { JFileChooser jfc = new JFileChooser(); jfc.setFileFilter(new PicuterFilter());//設置當前的文件過濾器! jfc.setAcceptAllFileFilterUsed(false);//設置所有文件過濾器不使用! //jfc.addChoosableFileFilter(new Filter()); jfc.showOpenDialog(null); //將輸入流按照下面方式處理, 根據Iterator<ImageReader> itImage是否能 //成功的返回一個ImageReader對象確認該流文件是否是一個圖片文件! //並ImageReader類中的getFormatName()得到文件的格式! //通過最后可以通過ImageIcon的byte[]構造函數建立ImageIcon對象! //最后將圖片顯示在面板上! File fl = jfc.getSelectedFile(); if(fl == null) return ; try{ InputStream is = new FileInputStream(fl); ImageInputStream iis = ImageIO.createImageInputStream(is); Iterator<ImageReader> itImage = ImageIO.getImageReaders(iis); if(itImage.hasNext()){ ImageReader reader = itImage.next(); byte[] imageByte = new byte[1024*64]; int len = iis.read(imageByte); if(len > 64 * 1000){ JOptionPane.showMessageDialog(new Frame(), "圖片過大!請采用文件傳輸!"); return ; } DatagramPacket dp = null; //先說明一下數據是誰發送過來的! byte[] ip = InetAddress.getLocalHost().getHostAddress().getBytes(); dp = new DatagramPacket(ip, ip.length, sendIAD, QQReceive.getPort()); ds.send(dp); try { Thread.sleep(100); } catch (InterruptedException e1) { } dp = new DatagramPacket("PICUTER".getBytes(), "PICUTER".getBytes().length, sendIAD, QQReceive.getPort()); ds.send(dp); try { Thread.sleep(100); } catch (InterruptedException e1) { } dp = new DatagramPacket(imageByte, len, sendIAD, QQReceive.getPort()); ds.send(dp); synchronized(QQ.class){ byte[] name = null; name = new String(InetAddress.getLocalHost().getHostName() + " : ").getBytes(); QQ.setTextPane(taReceive, name, name.length, QQ.PARAGRAPH, QQ.SEND); QQ.setTextPane(taReceive, imageByte, len, QQ.PICUTER, 0); } } else throw new NoPicuterException("不是一張圖片!"); }catch(IOException ex){ ex.printStackTrace(); } } }); setVisible(true); } public void setSendIAD(String ip) throws RuntimeException{ String[] ipStr = ip.split("\\.");//將字符串中的數字分離 byte[] ipBuf = new byte[4];//存儲IP的byte數組 for(int i = 0; i < 4; i++){ ipBuf[i] = (byte)(Integer.parseInt(ipStr[i])&0xff); } try { sendIAD = InetAddress.getByAddress(ipBuf); } catch (UnknownHostException e3) { throw new RuntimeException("IP錯誤(不存在或無發鏈接)!"); } } public DatagramSocket getDs(){ return ds; } public JTextPane getReceive(){ return taReceive; } } class PicuterFilter extends FileFilter { public boolean accept(File file){ return(file.isDirectory() || file.getName().endsWith(".gif") || file.getName().endsWith(".png") || file.getName().endsWith(".bmp") || file.getName().endsWith(".jpg") ); /* 返回要顯示的文件類型 */ /* * File.isDirectory()測試此抽象路徑名表示的文件是否是一個目錄 */ } public String getDescription() { return("Picuter Files(*.gif, *.png, *.jpg, *.bmp)"); //返回顯示文件類型的描述 } } class NoPicuterException extends IOException{ public NoPicuterException(String x){ super(x); } }

package testFour; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.util.Map; import java.util.Set; import javax.swing.JOptionPane; public class QQReceive implements Runnable{ private QQFrame fm = null; private static DatagramSocket ds = null; private static int port = 10101; private boolean flag = true; public void setFlag(){ if(ds != null) ds.close(); flag = false; } public QQReceive(){ try { //建立udp通信方式 ds = new DatagramSocket(port); } catch (SocketException e1) { port = -1; JOptionPane.showMessageDialog(null, "DatagramSocket端口綁定錯誤!", "綁定錯誤", JOptionPane.OK_CANCEL_OPTION); } } public static DatagramSocket getUdpSocket(){ return ds; } public static int getPort(){ return port; } public DatagramSocket getDS(){ return ds; } public void run(){ if(ds == null) return ; byte[] bt = new byte[10024*10]; DatagramPacket dp = new DatagramPacket(bt, bt.length); while(flag){ int flagx = 0; try { //ds.setReceiveBufferSize(size); ds.receive(dp); String ip = new String(dp.getData(), 0, dp.getLength()); ds.receive(dp); String tag = new String(dp.getData(), 0, dp.getLength()); ds.receive(dp); Map<String, QQFrame>mp = QQDialog.getMap(); Set<String> set = QQDialog.getSet(); fm = mp.get(ip); System.out.println(ip); if(set.contains(ip) && fm == null){//如果存在該好友,但是不存在對應的對話窗口 //自動產生對話窗口 Map<String, String> nameToIP = QQDialog.getNameToIP(); String[] ipStr = ip.split("\\.");//將字符串中的數字分離 byte[] ipBuf = new byte[4];//存儲IP的byte數組 for(int i = 0; i < 4; i++){ ipBuf[i] = (byte)(Integer.parseInt(ipStr[i])&0xff); } String name = null; for(String ss : nameToIP.keySet()) if( ip.equals(nameToIP.get(ss)) ){ name = ss; break; } fm = new QQFrame(name, ipBuf); }else if(fm != null){ fm.requestFocus(); } if( tag.equals("FILE") ) flagx = QQ.FILE; else if( tag.equals("PICUTER")) flagx = QQ.PICUTER; else if( tag.equals("PARAGRAPH")) flagx = QQ.PARAGRAPH; } catch (IOException e) { e.printStackTrace(); } synchronized(QQ.class){ if(fm == null) continue; byte[] x = new String(dp.getAddress().getHostName() + " : ").getBytes(); QQ.setTextPane(fm.getReceive(), x, x.length, QQ.PARAGRAPH, QQ.RECEIVE); QQ.setTextPane(fm.getReceive(), dp.getData(), dp.getLength(), flagx, QQ.RECEIVE*3); } } } }

package testFour; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Calendar; import java.util.Map; import java.util.Set; import javax.swing.JOptionPane; public class ServerSocketQQ implements Runnable{ private ServerSocket sst = null; private Socket st = null; private static int port = 10100; private boolean flag = true; public void setFlag(){ if(sst != null) try { sst.close(); } catch (IOException e) { e.printStackTrace(); } flag = false; } public static int getPort(){ return port; } public ServerSocketQQ(){ //建立服務端 try { sst = new ServerSocket(port); } catch (IOException e) { port = -1; JOptionPane.showMessageDialog(null, "ServerSocket端口綁定錯誤!", "綁定錯誤", JOptionPane.OK_CANCEL_OPTION); } } public void run(){ byte[] bt = null; int len = 0; if(sst == null) return ; while(true){ try{ //偵聽並接受到此服務套接字的連接。此方法在進行連接之前一直阻塞。 創建新套接字 st = sst.accept(); //得到客戶端傳輸過來的流 InputStream is = st.getInputStream(); OutputStream os = st.getOutputStream(); bt = new byte[1024]; len = is.read(bt); String name = new String(bt, 0, len); QQFrame fm = null; Map<String, QQFrame>mp = QQDialog.getMap(); Set<String> set = QQDialog.getSet(); fm = mp.get(name); if(set.contains(name) && fm == null){//如果存在該好友,但是不存在對應的對話窗口 //自動產生對話窗口 Map<String, String> nameToIP = QQDialog.getNameToIP(); fm = new QQFrame(name, nameToIP.get(name).getBytes()); } if(fm == null){//對方不存在該好友! st.close(); continue; } len = is.read(bt); String fileName = new String(bt, 0, len); int choose = JOptionPane.showConfirmDialog(null, "接受或不接受", "文件傳輸提示", JOptionPane.YES_NO_OPTION); if(choose == JOptionPane.NO_OPTION){ bt = new String(Calendar.getInstance().getTime().toString() + ":文件接受失敗!").getBytes(); QQ.setTextPane(fm.getReceive(), bt, bt.length, QQ.FILE, QQ.FILEX); st.close(); continue; } FileOutputStream fos = new FileOutputStream( new File(fileName) ); while( (len = is.read(bt)) != -1 ){//先將流文件變成byte[], 然后利用套接字的輸出流發送給客戶端 fos.write(bt); fos.flush(); } bt = new String(Calendar.getInstance().getTime().toString() + ":文件接受成功!").getBytes(); QQ.setTextPane(fm.getReceive(), bt, bt.length, QQ.FILE, QQ.FILEX); st.close(); }catch(IOException e){ e.printStackTrace(); } } } }