充分理解Socket
1.什麼是socket
所謂socket通常也稱作"通訊端",用於描述IP地址和連接埠,是一個通訊鏈的控制代碼。應用程式通常通過"通訊端"向網路發出請求或者應答網路請求。
以J2SDK-1.3為例,Socket和ServerSocket類庫位於java.net包中。ServerSocket用於伺服器端,Socket是建立網路連接時使用的。在串連成功時,應用程式兩端都會產生一個Socket執行個體,操作這個執行個體,完成所需的會話。對於一個網路連接來說,通訊端是平等的,並沒有差別,不因為在伺服器端或在用戶端而產生不同層級。不管是Socket還是ServerSocket它們的工作都是通過SocketImpl類及其子類完成的。
重要的Socket API:
java.net.Socket繼承於java.lang.Object,有八個構造器,其方法並不多,下面介紹使用最頻繁的三個方法,其它方法大家可以見JDK-1.3文檔。
. Accept方法用於產生"阻塞",直到接受到一個串連,並且返回一個用戶端的Socket對象執行個體。"阻塞"是一個術語,它使程式運行暫時"停留"在這個地方,直到一個會話產生,然後程式繼續;通常"阻塞"是由迴圈產生的。
.
getInputStream方法獲得網路連接輸入,同時返回一個IutputStream對象執行個體,。
. getOutputStream方法串連的另一端將得到輸入,同時返回一個OutputStream對象執行個體。
注意:其中getInputStream和getOutputStream方法均會產生一個IOException,它必須被捕獲,因為它們返回的流對象,通常都會被另一個流對象使用。
2.如何開發一個Server-Client模型的程式
開發原理:
伺服器,使用ServerSocket監聽指定的連接埠,連接埠可以隨意指定(由於1024以下的連接埠通常屬於保留連接埠,在一些作業系統中不可以隨意使用,所以建議使用大於1024的連接埠),等待客戶串連請求,客戶串連後,會話產生;在完成會話後,關閉串連。
用戶端,使用Socket對網路上某一個伺服器的某一個連接埠發出串連請求,一旦串連成功,開啟會話;會話完成後,關閉Socket。用戶端不需要指定開啟的連接埠,通常臨時的、動態分配一個1024以上的連接埠
簡單的聊天程式樣本。用到多線程、流、socket編程,用到Frame。
server端程式如下:
import java.io.BufferedReader;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.EOFException;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintStream;import java.net.BindException;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;import java.util.List;public class ChatServer { boolean started = false; ServerSocket ss = null;Socket s = null;String ip;List<Client> clients = new ArrayList<Client>();//int count;//開啟伺服器 public void startServer(){ try{ss = new ServerSocket(8888);started = true;System.out.println("伺服器已啟動!");} catch (BindException e1){System.out.println("連接埠被佔用,請關閉後重試");} catch (IOException e) {e.printStackTrace();}try { while(started){s = ss.accept();System.out.println("一個客戶已串連");Client c = new Client(s);new Thread(c).start();clients.add(c);}}catch (IOException e) {e.printStackTrace();}finally {try {ss.close();//關閉此通訊端。 在 accept() 中所有當前阻塞的線程都將會拋出 SocketException。 //如果此通訊端有一個與之關聯的通道,則關閉該通道。} catch (IOException e) {e.printStackTrace();}} }public static void main(String[] args) {new ChatServer().startServer();} class Client implements Runnable { private Socket s;private DataInputStream dis = null;private DataOutputStream dos = null;private boolean flag = false; public Client(Socket s){ this.s = s; try {dis = new DataInputStream(s.getInputStream());dos = new DataOutputStream(s.getOutputStream());flag = true;} catch (IOException e) {e.printStackTrace();} } @Override public void run() { //把目前使用者發來的資訊發送給所有使用者. try { while (flag) { String str = dis.readUTF();for (int i = 0; i < clients.size();i++) { Client c = clients.get(i); //獲得目前使用者的IP ip=s.getInetAddress().getHostAddress(); c.send(str); }} }catch (EOFException e) {System.out.println("用戶端關閉了!");} catch (IOException e) {e.printStackTrace();}finally {try {if(dis != null) dis.close();if(dos != null) dos.close();if(s != null) {s.close();}} catch (IOException e1) {e1.printStackTrace();}} } public void send(String str){ try{ dos.writeUTF(ip+"說:");dos.writeUTF(str+'\n');} catch (IOException e) {clients.remove(this);} } } }
client端代碼如下:
import java.awt.BorderLayout;import java.awt.Button;import java.awt.TextArea;import java.awt.TextField;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.EOFException;import java.io.IOException;import java.net.Socket;import java.net.SocketException;import java.net.UnknownHostException;import javax.swing.JFrame;public class ChatClient extends JFrame {TextArea textArea = new TextArea();// 建立一個文本域TextField textField = new TextField();// 建立一個文字框Button button_send = new Button("發送");Thread t = new Thread(new RecvThread());Socket s = null;DataInputStream dis = null;DataOutputStream dos = null;String str = null;boolean flag = false; public void launchFrame(){this.setLocation(400, 400);//this.setSize(300, 300);//button_send.setSize(20, 20);this.add(textArea,BorderLayout.NORTH);this.add(textField,BorderLayout.SOUTH);this.add(button_send,BorderLayout.EAST);//this.setResizable(true);pack();this.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent arg0) {disConnect();System.exit(0);}});//this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//匿名內部類對發送按鈕進行響應,發送資訊button_send.addActionListener(new ActionListener(){public void actionPerformed(ActionEvent e){str = textField.getText().trim();//textArea.setText(str);textField.setText("");try {dos.writeUTF(str);dos.flush();} catch (IOException e1) {e1.printStackTrace();}}});setVisible(true);connect();t.start();}//從伺服器接收資訊class RecvThread implements Runnable{@Overridepublic void run() {try {while(flag){str = dis.readUTF();textArea.setText(textArea.getText() + str + '\n');}}catch (SocketException e) {System.out.println("退出了,bye!");} catch (EOFException e) {System.out.println("退出了,bye - bye!");} catch (IOException e) {e.printStackTrace();} }}//串連到伺服器public void connect(){try {s = new Socket("127.0.0.1",8888);flag = true;System.out.println("已經串連到伺服器");dis = new DataInputStream(s.getInputStream());dos = new DataOutputStream(s.getOutputStream());} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}//中斷連線public void disConnect(){try {dis.close();dos.close();s.close();} catch(SocketException e2){System.out.println("用戶端關閉了,bye");}catch (EOFException e1){System.out.println("用戶端關閉了,bye");//catch還有先後順序啊?}catch (IOException e) {System.out.println("串連已經斷開");e.printStackTrace();}}public static void main(String[] args) {new ChatClient().launchFrame();}}
打包成JAR檔案方式:
在MyEclipse中,選中項目名,右擊--export--jar--打包成client和server兩個JAR檔案,啟動並執行時候,先運行server再運行client,IP設定為127.0.0.1,所以開啟兩個用戶端,自己和自己通訊。
假如我想和A通訊,那麼設定為A的IP,然後他開啟server和client,我也開啟一個server和client,就可以通訊了。
自己剛學這方面知識,所以程式只能簡單的通訊,有以下疑問:
1、我我每次關掉視窗,通訊結束的時候,只能通過殺進程的方式關閉麼。要不然下一次好像起不來啊?
2、QQ的原始碼哪裡可以下載到嗎?
3、是不是我這樣的程式用到的是TCP ,因為程式中有建立串連這個環節啊
4、不知道我打包JAR檔案的方式是否正確。不能把client和server打包在一起麼。。。。
第一個項目,所以各種迷惑。。。。。