20分!!!??(⊙o⊙)
發(fā)展壯大離不開(kāi)廣大客戶長(zhǎng)期以來(lái)的信賴(lài)與支持,我們將始終秉承“誠(chéng)信為本、服務(wù)至上”的服務(wù)理念,堅(jiān)持“二合一”的優(yōu)良服務(wù)模式,真誠(chéng)服務(wù)每家企業(yè),認(rèn)真做好每個(gè)細(xì)節(jié),不斷完善自我,成就企業(yè),實(shí)現(xiàn)共贏。行業(yè)涉及成都石涼亭等,在成都網(wǎng)站建設(shè)、營(yíng)銷(xiāo)型網(wǎng)站建設(shè)、WAP手機(jī)網(wǎng)站、VI設(shè)計(jì)、軟件開(kāi)發(fā)等項(xiàng)目上具有豐富的設(shè)計(jì)經(jīng)驗(yàn)。
給你這個(gè)做參考吧。自己改一下就行了。(共兩個(gè)文件)
//ChatClient.java
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
public class ChatClient extends Frame {
Socket s = null;
DataOutputStream dos = null;
DataInputStream dis = null;
private boolean bConnected = false;
TextField tfTxt = new TextField();
TextArea taContent = new TextArea();
Thread tRecv = new Thread(new RecvThread());
public static void main(String[] args) {
new ChatClient().launchFrame();
}
public void launchFrame() {
setLocation(400, 300);
this.setSize(300, 300);
add(tfTxt, BorderLayout.SOUTH);
add(taContent, BorderLayout.NORTH);
pack();
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent arg0) {
disconnect();
System.exit(0);
}
});
tfTxt.addActionListener(new TFListener());
setVisible(true);
connect();
tRecv.start();
}
public void connect() {
try {
s = new Socket("127.0.0.1", 8888);
dos = new DataOutputStream(s.getOutputStream());
dis = new DataInputStream(s.getInputStream());
System.out.println("connected!");
bConnected = true;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void disconnect() {
try {
dos.close();
dis.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
/*
try {
bConnected = false;
tRecv.join();
} catch(InterruptedException e) {
e.printStackTrace();
} finally {
try {
dos.close();
dis.close();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
*/
}
private class TFListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String str = tfTxt.getText().trim();
//taContent.setText(str);
tfTxt.setText("");
try {
//System.out.println(s);
dos.writeUTF(str);
dos.flush();
//dos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
private class RecvThread implements Runnable {
public void run() {
try {
while(bConnected) {
String str = dis.readUTF();
//System.out.println(str);
taContent.setText(taContent.getText() + str + '\n');
}
} catch (SocketException e) {
System.out.println("退出了,bye!");
} catch (EOFException e) {
System.out.println("推出了,bye - bye!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//ChatServer.java
import java.io.*;
import java.net.*;
import java.util.*;
public class ChatServer {
boolean started = false;
ServerSocket ss = null;
ListClient clients = new ArrayListClient();
public static void main(String[] args) {
new ChatServer().start();
}
public void start() {
try {
ss = new ServerSocket(8888);
started = true;
} catch (BindException e) {
System.out.println("端口使用中....");
System.out.println("請(qǐng)關(guān)掉相關(guān)程序并重新運(yùn)行服務(wù)器!");
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
try {
while(started) {
Socket s = ss.accept();
Client c = new Client(s);
System.out.println("a client connected!");
new Thread(c).start();
clients.add(c);
//dis.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
ss.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Client implements Runnable {
private Socket s;
private DataInputStream dis = null;
private DataOutputStream dos = null;
private boolean bConnected = false;
public Client(Socket s) {
this.s = s;
try {
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
bConnected = true;
} catch (IOException e) {
e.printStackTrace();
}
}
public void send(String str) {
try {
dos.writeUTF(str);
} catch (IOException e) {
clients.remove(this);
System.out.println("對(duì)方退出了!我從List里面去掉了!");
//e.printStackTrace();
}
}
public void run() {
try {
while(bConnected) {
String str = dis.readUTF();
System.out.println(str);
for(int i=0; iclients.size(); i++) {
Client c = clients.get(i);
c.send(str);
//System.out.println(" a string send !");
}
/*
for(IteratorClient it = clients.iterator(); it.hasNext(); ) {
Client c = it.next();
c.send(str);
}
*/
/*
IteratorClient it = clients.iterator();
while(it.hasNext()) {
Client c = it.next();
c.send(str);
}
*/
}
} catch (EOFException e) {
System.out.println("Client closed!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(dis != null) dis.close();
if(dos != null) dos.close();
if(s != null) {
s.close();
//s = null;
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
這個(gè)應(yīng)該和你框架沒(méi)多大關(guān)系吧 ,又不走action, 在web.xml加入一個(gè)自己的listener,在里面創(chuàng)建一個(gè)tmer實(shí)現(xiàn)定時(shí),定時(shí)任務(wù) 實(shí)時(shí)獲取某人的ip, 采用socket或httpcon向這個(gè)地址寫(xiě)數(shù)據(jù)流,某人的客戶端接到通知 并接收完數(shù)據(jù),入庫(kù),然后觸發(fā)一個(gè)事件,將數(shù)據(jù)丟給前臺(tái) js, js去把消息顯示出來(lái)。
使用7z格式壓縮上傳
下載1:
下載2: att.newsmth.net/att.php?p.75.25665.766.7z
Java版源代碼下載:
(有些網(wǎng)友對(duì)C++如何實(shí)現(xiàn)感興趣,推薦一下Poco帶的WebSocket功能,把Java源代碼翻譯成C++就行了)
1. 說(shuō)明:
utf8版本,支持各種語(yǔ)言版本的windows系統(tǒng)
程序內(nèi)嵌數(shù)據(jù)庫(kù)
用戶帳號(hào)非明文存儲(chǔ)
在Firefox/Chrome瀏覽器測(cè)試通過(guò),建議使用Chrome,F(xiàn)irefox不支持mp3的消息提示音
2. 現(xiàn)有功能
注冊(cè)/登錄/搜索/添加好友(需要雙方互相添加對(duì)方為好友,才能互相聊天)
抖動(dòng)窗口
兩種狀態(tài),登錄/離線
3.可擴(kuò)展的功能
使用Windows域用戶帳號(hào),無(wú)需注冊(cè)
收發(fā)離線信息
查看歷史信息
新消息提示
群聊
共享文件
4. (可能)存在的問(wèn)題
并發(fā)性未作充分測(cè)試
添加好友的確認(rèn)
5. 適用的場(chǎng)景
學(xué)校
小公司
6.使用
解壓后雙擊執(zhí)行start.bat,將自動(dòng)進(jìn)入初始界面,如下圖
默認(rèn)使用80端口,若80端口已被占用,修改start.bat中的setserver_port=80
雙擊左上角圖標(biāo),彈出對(duì)話框,可以選擇注冊(cè)
注冊(cè)成功
登錄
搜索好友,如不提供搜索條件,則返回所有已注冊(cè)用戶
添加好友
添加好友提示
聊天
這里有一個(gè)簡(jiǎn)單的模擬通訊 要先運(yùn)行服務(wù)器端 再運(yùn)行客戶端 否則會(huì)報(bào)錯(cuò):
服務(wù)器端代碼:
package?com.test3;
import?java.net.*;
import?java.io.*;
import?javax.swing.*;
import?java.awt.*;
import?java.awt.event.*;
public?class?Server2?extends?JFrame?implements?ActionListener?,?KeyListener?{
JTextArea?jta=null;
JScrollPane?jsp=null;
JTextField?jtf=null;
JButton?jb=null;
JPanel?jp=null;
InputStreamReader?isr=null;
BufferedReader?br=null;
PrintWriter?pw=null;
Socket?s;
String?jtatext="";
public?static?void?main(String[]?args)?{
//?TODO?Auto-generated?method?stub
Server2?sv2=new?Server2();
}
public?Server2(){
jta=new?JTextArea();
jta.setEditable(false);
jsp=new?JScrollPane(jta);
jtf=new?JTextField(10);
jtf.addKeyListener(this);
jb=new?JButton("發(fā)送");
jb.addActionListener(this);
jp=new?JPanel();
jp.add(jtf);
jp.add(jb);
this.add(jsp,"Center");
this.add(jp,"South");
this.setSize(300,300);
this.setLocationRelativeTo(this);
this.setTitle("服務(wù)器端");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
try{
ServerSocket?ss=new?ServerSocket(9999);
s=ss.accept();
isr=new?InputStreamReader(s.getInputStream());
br=new?BufferedReader(isr);
pw=new?PrintWriter(s.getOutputStream(),true);
while(true){
String?info=br.readLine();
jta.append("客戶端對(duì)服務(wù)器說(shuō):???"+info+"\r\n");
// this.jta.setText(jtatext);
}
}catch(Exception?e){
e.printStackTrace();
}
}
@Override
public?void?actionPerformed(ActionEvent?e)?{
if(e.getSource()==jb){
try?{
pw.println(jtf.getText());
jta.append("服務(wù)器對(duì)客戶端說(shuō):???"+jtf.getText()+"\r\n");
// jta.setText(jtatext);
jtf.setText("");
}?catch?(Exception?e1)?{
//?TODO?Auto-generated?catch?block
e1.printStackTrace();
}
}
}
@Override
public?void?keyTyped(KeyEvent?e)?{}
@Override
public?void?keyPressed(KeyEvent?e)?{
if(e.getKeyCode()==KeyEvent.VK_ENTER){
try?{
pw.println(jtf.getText());
jta.append("服務(wù)器對(duì)客戶端說(shuō):???"+jtf.getText()+"\r\n");
jtf.setText("");
}?catch?(Exception?e1)?{
e1.printStackTrace();
}
}
}
@Override
public?void?keyReleased(KeyEvent?e)?{}
}
客戶端代碼:
package?com.test3;
import?java.net.*;
import?java.io.*;
import?javax.swing.*;
import?java.awt.*;
import?java.awt.event.*;
public?class?Client2?extends?JFrame?implements?ActionListener?,KeyListener?{
JTextArea?jta=null;
JScrollPane?jsp=null;
JTextField?jtf=null;
JButton?jb=null;
JPanel?jp=null;
String?jtatext="";
Socket?s;
PrintWriter?pw=null;
InputStreamReader?isr=null;
BufferedReader?br=null;
public?static?void?main(String[]?args)?{
//?TODO?Auto-generated?method?stub
Client2?sv2=new?Client2();
}
public?Client2(){
jta=new?JTextArea();
jta.setEditable(false);
jsp=new?JScrollPane(jta);
jtf=new?JTextField(10);
jtf.addKeyListener(this);
jb=new?JButton("發(fā)送");
jb.addActionListener(this);
jp=new?JPanel();
jp.add(jtf);
jp.add(jb);
this.add(jsp,"Center");
this.add(jp,"South");
this.setSize(300,300);
this.setLocationRelativeTo(this);
this.setTitle("客戶端");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
try?{
?s=new?Socket("127.3.3.3",9999);
?isr=new?InputStreamReader(s.getInputStream());
?br=new?BufferedReader(isr);
?pw=new?PrintWriter(s.getOutputStream(),true);
?
?while(true){
?String?info=br.readLine();?
jta.append("服務(wù)器對(duì)客戶端說(shuō):???"+info+"\r\n");
?
?}
}?catch?(Exception?e)?{
//?TODO?Auto-generated?catch?block
e.printStackTrace();
}
}
@Override
public?void?actionPerformed(ActionEvent?e)?{
if(e.getSource()==jb){
try?{
pw.println(this.jtf.getText());
jta.append("客戶端對(duì)服務(wù)器說(shuō):???"+jtf.getText()+"\r\n");
jtf.setText("");
}?catch?(Exception?e1)?{
//?TODO?Auto-generated?catch?block
e1.printStackTrace();
}
}
}
public?void?keyTyped(KeyEvent?e)?{}
public?void?keyPressed(KeyEvent?e)?{
if(e.getKeyCode()==KeyEvent.VK_ENTER){
try?{
pw.println(this.jtf.getText());
jta.append("客戶端對(duì)服務(wù)器說(shuō):???"+jtf.getText()+"\r\n");
jtf.setText("");
}?catch?(Exception?e1)?{
//?TODO?Auto-generated?catch?block
e1.printStackTrace();
}
}
}
public?void?keyReleased(KeyEvent?e)?{}
}
一、Java軟件加密基本思路
對(duì)于應(yīng)用軟件的保護(hù)筆者從兩個(gè)方面進(jìn)行考慮,第一是阻止盜版使用軟件,第二是阻止競(jìng)爭(zhēng)對(duì)手對(duì)軟件反編譯,即阻止對(duì)軟件的逆向工程。
1、阻止盜版
在軟件運(yùn)行時(shí)對(duì)自身存在的合法性進(jìn)行判斷,如果認(rèn)為自身的存在和運(yùn)行是被授權(quán)的、合法的,就運(yùn)行;否則終止運(yùn)行。這樣即使軟件可以被隨意復(fù)制,只要盜版用戶沒(méi)有相應(yīng)的授權(quán)信息就無(wú)法使用軟件。
2、阻止反編譯
對(duì)編譯產(chǎn)生的Class文件加密處理,并在運(yùn)行時(shí)進(jìn)行解密,解密者無(wú)法對(duì)軟件進(jìn)行反編譯。
二、Java軟件加密的總體流程
為了保護(hù)用Java語(yǔ)言開(kāi)發(fā)的軟件,我們?cè)O(shè)計(jì)并實(shí)現(xiàn)了一個(gè)實(shí)用、高強(qiáng)度的加密算法。以下稱(chēng)需要保護(hù)的Java軟件為“受保護(hù)程序”,稱(chēng)對(duì)“受保護(hù)程序”進(jìn)行加密保護(hù)的軟件為“加密程序”。對(duì)軟件加密保護(hù)的流程如圖1所示。
三、加密算法分析設(shè)計(jì)
1、用戶信息提取器設(shè)計(jì)
為了防止用戶發(fā)布序列號(hào)而導(dǎo)致“一次發(fā)行,到處都是”的盜版問(wèn)題,提取用戶機(jī)器中硬件相關(guān)的、具有唯一性的信息——用戶計(jì)算機(jī)的硬盤(pán)分區(qū)C的序列號(hào),并要求用戶將此信息與用戶名一起返回,之后用“序列號(hào)生成器”根據(jù)用戶返回信息生成一個(gè)唯一合法的軟件注冊(cè)序列號(hào)發(fā)回用戶,用戶即可使用此號(hào)碼注冊(cè)使用軟件。
這個(gè)信息提取器使用Winclows 32匯編以一個(gè)獨(dú)立的小程序方式實(shí)現(xiàn),程序代碼如圖2所示。
2、序列號(hào)生成器與序列號(hào)合法性判斷函數(shù)的設(shè)計(jì)
序列號(hào)生成器與序列號(hào)合法性判斷函數(shù)中運(yùn)用RSA加密算法。在序列號(hào)生成器中是使用私鑰將用戶返回的信息(硬盤(pán)序列號(hào),用戶名)進(jìn)行加密得到相應(yīng)的注冊(cè)序列號(hào);在序列號(hào)合法性判斷函數(shù)中使用私鑰將用戶輸入的注冊(cè)序列號(hào)解密,再與(硬盤(pán)序列號(hào),用戶名)進(jìn)行比較,一致則調(diào)用程序裝載器將程序其他部分解密裝入內(nèi)存,初始化刪環(huán)境并運(yùn)行程序主體;否則退出。
RSA加密算法的實(shí)現(xiàn)需要使用大數(shù)運(yùn)算庫(kù),我們使用MIRACL大數(shù)庫(kù)來(lái)實(shí)現(xiàn)RSA計(jì)算,序列號(hào)生成器的主要代碼如下:
char szlnputString[]=”機(jī)器碼和用戶名組成的字符串”;
char szSerial[256]=[0];//用于存放生成的注冊(cè)碼
bign,d,c,m; //MIRACL中的大數(shù)類(lèi)型
mip→IBASE=16; //以16進(jìn)制模式
n= mlrvar(0); //初始化大數(shù)
d= mirvar(0);
c= mirvar(0); //C存放輸入的字符串大數(shù)
m= mlrva(o);
bytes to big( len, szlnputString,c);
//將輸入字符串轉(zhuǎn)換成大數(shù)形式并存入變量c中
cinstr(n,”以字符串形成表示的模數(shù)”);//初始化模數(shù)
cinstr(d,”以字符串形成表示的公鑰”)://初始化公鑰
powmod(c,d,n,m); //計(jì)算m=cdmod n
cotstr(m,szSerial);//m的16進(jìn)制字符串即為注冊(cè)碼
序列號(hào)合法性檢測(cè)函數(shù)的主要代碼如下:
char szlnputStringL]=”機(jī)器碼和用戶名組成的字符串”;
char szSerial[ 256]=”用戶輸入的序列號(hào)”
bign,e,c,m; //MIRACL中的大數(shù)類(lèi)型
mip→IBASE=16; //以16進(jìn)制模式
cinstr(m,szSerial); //將序列號(hào)的16進(jìn)制轉(zhuǎn)成大數(shù)形式
cinstr(n,”模數(shù)n的字符串形式”);//初始化模數(shù)n
cinstr(e,”字符串形式的公鑰”);//初始化公鑰
if compare(m,n)==-1) //mn時(shí)才進(jìn)行解密
{
powmod(m,e,n,c);//計(jì)算m=me mod n
big_to _bytes(0,c,szSerial,0); //轉(zhuǎn)為字符串
return lstrcmp( szlnputString,szSerial);
}
3、強(qiáng)耦合關(guān)系的設(shè)計(jì)
如果在序列號(hào)合法性檢測(cè)函數(shù)中簡(jiǎn)單地使用圖3所示流程:
解密者可以使用以下幾種手段進(jìn)行攻擊:
(1)修改“判斷合法性子函數(shù)”的返回指令,讓它永遠(yuǎn)返回正確值,這樣可以使用任意的序列號(hào),安裝/使用軟件。
(2)修改判斷后的跳轉(zhuǎn)指令,使程序永遠(yuǎn)跳到正確的分支運(yùn)行,效果和上一種一樣。
(3)在“判斷合法性子函數(shù)”之前執(zhí)行一條跳轉(zhuǎn)指令,繞過(guò)判斷,直接跳轉(zhuǎn)到“正常執(zhí)行”分支運(yùn)行,這樣可以不用輸入序列號(hào)安裝/使用軟件。
為阻止以上攻擊手段,筆者在程序中增加了“序列號(hào)合法性檢測(cè)函數(shù)”與程序其他部分“強(qiáng)耦合”(即增強(qiáng)其與程序其他部分的關(guān)聯(lián)度,成為程序整體密不可分的一部分,一旦被修改程序?qū)o(wú)法正常工作)的要求(見(jiàn)圖1),并且設(shè)置一個(gè)“完整性檢測(cè)函數(shù)”用于判斷相關(guān)的代碼是否被修改過(guò)。當(dāng)然,基于同樣的原因,“完整性檢測(cè)函數(shù)”也必須與程序其他部分存在“強(qiáng)耦合”關(guān)系。
強(qiáng)耦合關(guān)系通過(guò)以下方式建立:
在程序其他部分的函數(shù)(例如函數(shù)A)中隨機(jī)的訪問(wèn)需要強(qiáng)耦合的“序列號(hào)合法性檢測(cè)函數(shù)”和“完整性檢測(cè)函數(shù)”,在調(diào)用時(shí)隨機(jī)的選擇使用一個(gè)錯(cuò)誤的序列號(hào)或是用戶輸入的序列號(hào),并根據(jù)返回結(jié)果選擇執(zhí)行A中正常的功能代碼還是錯(cuò)誤退出的功能代碼,流程如圖4所示。
經(jīng)過(guò)這種改進(jìn),如果破解者通過(guò)修改代碼的方式破解將因“完整性檢測(cè)”失敗導(dǎo)致程序退出;如果使用SMC等技術(shù)繞過(guò)“序列號(hào)合法性判斷函數(shù)”而直接跳至序列號(hào)正確時(shí)的執(zhí)行入口,在后續(xù)的運(yùn)行中,將因?yàn)殡S機(jī)的耦合調(diào)用失敗導(dǎo)致程序退出。破解者要破解軟件將不得不跟蹤所有進(jìn)行了耦合調(diào)用的函數(shù),這顯然是一個(gè)艱巨的任務(wù)。
4、完整性檢測(cè)函數(shù)的設(shè)計(jì)
我們使用CRC算法算出需進(jìn)行完整性檢測(cè)的文件的校驗(yàn)碼,并用RSA加密算法的公鑰(不同于序列號(hào)合法性檢測(cè)中的公鑰/私鑰對(duì))將其加密存放在特定的文件中,在檢測(cè)時(shí)先用CRC算法重新生成需進(jìn)行完
整性檢測(cè)的文件的校驗(yàn)碼,并用私鑰將保存的校驗(yàn)碼解密,兩者相比較,相等則正常運(yùn)行;否則退出。
5、程序加載器的設(shè)計(jì)
與編譯成機(jī)器碼執(zhí)行的程序不同,Java程序只能由Java虛擬機(jī)解釋執(zhí)行,因此程序加載器的工作包括:初始化Java虛擬機(jī);在內(nèi)存中解密當(dāng)前要運(yùn)行的class文件;使解密后的c:lass文件在虛擬機(jī)中運(yùn)行,在
需要時(shí)解密另一個(gè)class文件。圖5是用于初始化JVM的代碼:
以上介紹了我們?cè)O(shè)計(jì)的針對(duì)Java軟件的加密保護(hù)方法,其中綜合運(yùn)用了多種加密技術(shù),抗破解強(qiáng)度高;使用純軟件保護(hù)技術(shù),成本低。經(jīng)筆者在Windows系列平臺(tái)上進(jìn)行測(cè)試,運(yùn)行穩(wěn)定,效果良好。
在研宄開(kāi)發(fā)過(guò)程中,我們還總結(jié)出加密保護(hù)軟件的一些經(jīng)驗(yàn):
1、對(duì)關(guān)鍵代碼和數(shù)據(jù)要靜態(tài)加密,再動(dòng)態(tài)解密執(zhí)行;要結(jié)合具體的工作平臺(tái)使用反跟蹤/調(diào)試技術(shù);
2、要充分利用系統(tǒng)的功能,如在Windows下使用DLL文件或驅(qū)動(dòng)程序形式能得到最大的豐又限,可以充分利用系統(tǒng)具有的各種功能;
3、如果可能應(yīng)該將關(guān)鍵代碼存放在不可禚復(fù)制的地方;
4、序列號(hào)要與機(jī)器碼等用戶信息相關(guān)以阻止鹽復(fù)布序列號(hào);
5、加密流程的合理性比加密算法本身的強(qiáng)度更重要。
package service_client_for_many;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
* 雙工服務(wù)器,多人 本服務(wù)器默認(rèn)不提供服務(wù),而是在客戶端連接時(shí)創(chuàng)建獨(dú)立線程負(fù)責(zé)業(yè)務(wù)
**/
public class MutilServer implements ActionListener {
private JFrame frame;
/** 邊界布局的主面板 */
private JPanel panelMain;
private JPanel panelDown;
private JTextArea ta;
private JTextField txt;
private JButton but;
private JScrollPane jsp;
private Font font;
/**
* 當(dāng)前服務(wù)器使用端口
*/
private int port = 6666;
/**
* 遠(yuǎn)程客戶端的IP
*/
private String clientIp;
/**
* 記錄所有正在工作的服務(wù)員的登記表
*/
private VectorWaiter dengJiBiao;
public MutilServer() {
frame = new JFrame("雙工多人服務(wù)器");
panelMain = new JPanel(new BorderLayout());
panelDown = new JPanel();
ta = new JTextArea();
txt = new JTextField(20);
but = new JButton("發(fā)送");
jsp = new JScrollPane(ta);
// 粘貼界面
panelDown.add(txt);
panelDown.add(but);
panelMain.add(jsp, BorderLayout.CENTER);
panelMain.add(panelDown, BorderLayout.SOUTH);
// 字體
font = new Font("宋體", Font.BOLD, 18);
txt.setFont(font);
ta.setFont(font);
but.setFont(font);
// 文本域只讀
ta.setEditable(false);
// 按鈕添加監(jiān)聽(tīng)
but.addActionListener(this);
frame.add(panelMain);
frame.setBounds(100, 300, 400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 關(guān)閉窗體時(shí)結(jié)束程序
frame.setAlwaysOnTop(true);// 永遠(yuǎn)在所有窗體最上
frame.setVisible(true);
// 創(chuàng)建登記表
dengJiBiao = new VectorWaiter();
// 光標(biāo)給消息文本框
txt.requestFocus();
createServer();
}
/**
* 顯示文本到文本域,并且追加一個(gè)換行
*
* @param msg
* 要顯示的文本
*/
public void showTxt(String msg) {
ta.append(msg + "\n");
}
public static void main(String[] args) {
new MutilServer();
}
// 動(dòng)作監(jiān)聽(tīng)
public void actionPerformed(ActionEvent e) {
if (e.getSource() == but) {// 發(fā)送
txt.requestFocus();
String str = txt.getText().trim();
if(str.length()==0){
showTxt("不可以發(fā)送空消息");
return;
}
if(dengJiBiao.size()==0){
showTxt("當(dāng)前木有客戶連接,無(wú)法發(fā)送信息");
return;
}
str ="服務(wù)器消息:"+str;
//找到所有登記表中的服務(wù)員,實(shí)現(xiàn)群發(fā)
for (int i = 0; i dengJiBiao.size(); i++) {
Waiter w = dengJiBiao.get(i);//得到當(dāng)前循環(huán)的服務(wù)員
w.send(str);
}
// 清空文本框,得到焦點(diǎn)
txt.setText("");
}
}
/**
* 啟動(dòng)網(wǎng)絡(luò)服務(wù)器
*/
public void createServer() {
showTxt("正在啟動(dòng)服務(wù)器,使用本機(jī)" + port + "端口...");
try {
ServerSocket server = new ServerSocket(port);
showTxt("服務(wù)器啟動(dòng)成功,開(kāi)始監(jiān)聽(tīng)網(wǎng)絡(luò)連接...");
while (true) {
Socket jiaoYi = server.accept();
// 每得到一個(gè)交易,就是來(lái)了一個(gè)客戶端.需要交給一個(gè)新的服務(wù)員去維護(hù)處理
new Waiter(jiaoYi, dengJiBiao, this);
}
} catch (IOException e) {
showTxt("服務(wù)器啟動(dòng)失敗,可能端口被占用");
}
}
}
package service_client_for_many;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/** 客戶端雙工 */
public class MyClient implements ActionListener{
private JFrame frame;
/** 邊界布局的主面板 */
private JPanel panelMain;
private JPanel panelDown;
private JTextArea ta;
private JTextField txt;
private JButton but;
private JScrollPane jsp;
private Font font;
/**
* 服務(wù)器IP
*/
private String ip = "192.168.10.239";
/**
* 服務(wù)器端口
*/
private int port = 6666;
private BufferedReader br;
private BufferedWriter bw;
public MyClient() {
frame = new JFrame("雙工客戶端1");
panelMain = new JPanel(new BorderLayout());
panelDown = new JPanel();
ta = new JTextArea();
txt = new JTextField(20);
but = new JButton("發(fā)送");
jsp = new JScrollPane(ta);
// 粘貼界面
panelDown.add(txt);
panelDown.add(but);
panelMain.add(jsp, BorderLayout.CENTER);
panelMain.add(panelDown, BorderLayout.SOUTH);
// 字體
font = new Font("宋體", Font.BOLD, 18);
txt.setFont(font);
ta.setFont(font);
but.setFont(font);
// 文本域只讀
ta.setEditable(false);
//按鈕添加監(jiān)聽(tīng)
but.addActionListener(this);
frame.add(panelMain);
frame.setBounds(500, 200, 400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 關(guān)閉窗體時(shí)結(jié)束程序
frame.setAlwaysOnTop(true);// 永遠(yuǎn)在所有窗體最上
frame.setVisible(true);
// 光標(biāo)給消息文本框
txt.requestFocus();
linkServer();
}
/** 顯示文本到文本域,并且追加一個(gè)換行
* @param msg 要顯示的文本
*/
public void showTxt(String msg) {
ta.append(msg+"\n");
}
public static void main(String[] args) {
new MyClient();
}
//動(dòng)作監(jiān)聽(tīng)
public void actionPerformed(ActionEvent e) {
if (e.getSource() == but) {// 發(fā)送
if (bw == null) {
showTxt("當(dāng)前沒(méi)有客戶端連接,無(wú)法發(fā)送消息");
return;
}
String s = txt.getText().trim();// 得到文本框要發(fā)送的文字,去掉兩端空格
if (s.length() == 0) {
showTxt("不可以發(fā)送空消息");
return;
}
showTxt("我說(shuō):" + s);
try {
bw.write(s + "\n");// 發(fā)送網(wǎng)絡(luò)消息給對(duì)方
bw.flush();// 清空緩沖
} catch (IOException e1) {
showTxt("信息:" + s + " 發(fā)送失敗");
}
// 清空文本框,得到焦點(diǎn)
txt.setText("");
txt.requestFocus();
}
}
/**
* 連接服務(wù)器
*/
public void linkServer(){
showTxt("準(zhǔn)備連接服務(wù)器"+ip+":"+port);
try {
Socket jiaoYi = new Socket(ip,port);
showTxt("連接服務(wù)器成功,開(kāi)始進(jìn)行通訊");
// 得到輸入和輸出
br = new BufferedReader(new InputStreamReader(
jiaoYi.getInputStream()));
bw = new BufferedWriter(new OutputStreamWriter(
jiaoYi.getOutputStream()));
String s = null;
while ((s = br.readLine()) != null) {
showTxt( s);
}
} catch (UnknownHostException e) {
showTxt("連接服務(wù)器失敗,網(wǎng)絡(luò)連通錯(cuò)誤");
} catch (IOException e) {
showTxt("與服務(wù)器通訊失敗,已經(jīng)斷開(kāi)連接");
}
showTxt("關(guān)閉");
}
}
package service_client_for_many;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/** 客戶端雙工 */
public class MyClient2 implements ActionListener{
private JFrame frame;
/** 邊界布局的主面板 */
private JPanel panelMain;
private JPanel panelDown;
private JTextArea ta;
private JTextField txt;
private JButton but;
private JScrollPane jsp;
private Font font;
/**
* 服務(wù)器IP
*/
private String ip = "192.168.10.239";
/**
* 服務(wù)器端口
*/
private int port = 6666;
private BufferedReader br;
private BufferedWriter bw;
public MyClient2() {
frame = new JFrame("雙工客戶端2");
panelMain = new JPanel(new BorderLayout());
panelDown = new JPanel();
ta = new JTextArea();
txt = new JTextField(20);
but = new JButton("發(fā)送");
jsp = new JScrollPane(ta);
// 粘貼界面
panelDown.add(txt);
panelDown.add(but);
panelMain.add(jsp, BorderLayout.CENTER);
panelMain.add(panelDown, BorderLayout.SOUTH);
// 字體
font = new Font("宋體", Font.BOLD, 18);
txt.setFont(font);
ta.setFont(font);
but.setFont(font);
// 文本域只讀
ta.setEditable(false);
//按鈕添加監(jiān)聽(tīng)
but.addActionListener(this);
frame.add(panelMain);
frame.setBounds(900, 200, 400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 關(guān)閉窗體時(shí)結(jié)束程序
frame.setAlwaysOnTop(true);// 永遠(yuǎn)在所有窗體最上
frame.setVisible(true);
// 光標(biāo)給消息文本框
txt.requestFocus();
linkServer();
}
/** 顯示文本到文本域,并且追加一個(gè)換行
* @param msg 要顯示的文本
*/
public void showTxt(String msg) {
ta.append(msg+"\n");
}
public static void main(String[] args) {
new MyClient2();
}
//動(dòng)作監(jiān)聽(tīng)
public void actionPerformed(ActionEvent e) {
if (e.getSource() == but) {// 發(fā)送
if (bw == null) {
showTxt("當(dāng)前沒(méi)有客戶端連接,無(wú)法發(fā)送消息");
return;
}
String s = txt.getText().trim();// 得到文本框要發(fā)送的文字,去掉兩端空格
if (s.length() == 0) {
showTxt("不可以發(fā)送空消息");
return;
}
showTxt("我說(shuō):" + s);
try {
bw.write(s + "\n");// 發(fā)送網(wǎng)絡(luò)消息給對(duì)方
bw.flush();// 清空緩沖
} catch (IOException e1) {
showTxt("信息:" + s + " 發(fā)送失敗");
}
// 清空文本框,得到焦點(diǎn)
txt.setText("");
txt.requestFocus();
}
}
/**
* 連接服務(wù)器
*/
public void linkServer(){
showTxt("準(zhǔn)備連接服務(wù)器"+ip+":"+port);
try {
Socket jiaoYi = new Socket(ip,port);
showTxt("連接服務(wù)器成功,開(kāi)始進(jìn)行通訊");
// 得到輸入和輸出
br = new BufferedReader(new InputStreamReader(
jiaoYi.getInputStream()));
bw = new BufferedWriter(new OutputStreamWriter(
jiaoYi.getOutputStream()));
String s = null;
while ((s = br.readLine()) != null) {
showTxt(s);
}
} catch (UnknownHostException e) {
showTxt("連接服務(wù)器失敗,網(wǎng)絡(luò)連通錯(cuò)誤");
} catch (IOException e) {
showTxt("與服務(wù)器通訊失敗,已經(jīng)斷開(kāi)連接");
}
showTxt("關(guān)閉");
}
}
package service_client_for_many;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Vector;
/**
* 服務(wù)員,線程類(lèi)
* 在客戶端連接后創(chuàng)建啟動(dòng)
* 負(fù)責(zé)當(dāng)前客戶端的所有數(shù)據(jù)收發(fā)
* 并且在業(yè)務(wù)需要時(shí),將結(jié)果與服務(wù)器(老板)進(jìn)行報(bào)告
*/
public class Waiter extends Thread{
private Socket sc;
private VectorWaiter dengJiBiao ;
private MutilServer server;
/**
* 客戶端IP
*/
private String ip;
private BufferedReader br;
private BufferedWriter bw;
/** 創(chuàng)建一個(gè)的新的服務(wù)員,負(fù)責(zé)當(dāng)前傳遞的客戶端連接(交易)
* 啟動(dòng)新線程
* @param sc 負(fù)責(zé)的交易
* @param dengJiBiao所有正在工作的服務(wù)員(所有交易)
* @param server 老板,也就是服務(wù)器
*/
public Waiter(Socket sc, VectorWaiter dengJiBiao,
MutilServer server) {
this.sc = sc;
this.dengJiBiao = dengJiBiao;
this.server = server;
//初始化連接的準(zhǔn)備工作
ip = sc.getInetAddress().getHostAddress();
// 得到輸入和輸出
try {
br = new BufferedReader(new InputStreamReader(
sc.getInputStream()));
bw = new BufferedWriter(new OutputStreamWriter(
sc.getOutputStream()));
} catch (IOException e) {
this.server.showTxt("與客戶端:"+ip+"建立通訊失敗");
e.printStackTrace();
return;//無(wú)效客戶,不再繼續(xù)
}
this.server.showTxt("客戶端:"+ip+"連接服務(wù)器成功");
//啟動(dòng)線程
this.start();
}
//線程
public void run(){
//開(kāi)始時(shí),登記開(kāi)工
dengJiBiao.addElement(this);
System.out.println(this.getClass().getName());
try {
String s = null;
while ((s = br.readLine()) != null) {
server.showTxt("客戶"+ip+"說(shuō):" + s);
//當(dāng)前客戶發(fā)來(lái)的信息,其它客戶也要能看得見(jiàn).需要實(shí)現(xiàn)轉(zhuǎn)發(fā)
//從登記表找到所有正在干活的服務(wù)員
for (int i = 0; i dengJiBiao.size(); i++) {
Waiter w = dengJiBiao.get(i);
//排除掉當(dāng)前服務(wù)員自己
if(w!=this)
w.send("客戶"+ip+"說(shuō):" + s);
}
}
} catch (Exception e) {
server.showTxt("客戶"+ip+"已經(jīng)離開(kāi)");
}
//結(jié)束時(shí),登記下班
dengJiBiao.removeElement(this);
}
/** 發(fā)送信息給當(dāng)前服務(wù)員負(fù)責(zé)的客戶端
* @param msg
*/
public void send(String msg){
try {
bw.write(msg+"\n");
bw.flush();
} catch (Exception e) {
server.showTxt("給客戶:"+ip+"發(fā)送信息"+msg+"失敗");
}
}
}
一個(gè)服務(wù)器類(lèi)·兩個(gè)客戶端類(lèi),一個(gè)線程類(lèi)負(fù)責(zé)收發(fā)