1. 程式人生 > >Java Socket網路程式設計

Java Socket網路程式設計

Java Socket網路程式設計

計算機網路中,客戶機與伺服器之間進行通訊時,客戶機必須知道伺服器的IP地址和埠號。IP地址是標識Internet網路中的某臺計算機,而埠號則標識著在伺服器上執行的某個程式(程序),如果在伺服器上執行的程式,沒有埠號,則客戶端的程式就不能找到它,也不能和它進行通訊。一定要清楚,別和電腦上的物理埠號搞混了。 埠號是一個邏輯的地址,用於標識計算機中執行著的應用程式。它可以是0~65535之間的一個數,0~1023為系統保留使用,例如http的是80埠等,這些是系統已經分配給預設服務使用的。 當兩個應用程式需要通過網路通訊時,就可以使用IP地址和埠號進行通過,IP地址和埠號組合在一起就是一個Socket(網路套接字)。套接字分為服務端的和客戶端的兩種

一、客戶端套接字

客戶端的套接字用於與伺服器端的套接字進行連線。其使用方法如下:

socket=new Socket("127.0.0.1",2018);

其中127.0.0.1標識伺服器端的IP地址,2018標識伺服器端應用程式的埠號。在建立套接字的過程中,可能會發生IOException異常,要注意進行處理。

二、伺服器端套接字

客戶端與伺服器端通訊時,伺服器端負責提供服務,客戶端通過Socket向伺服器端請求服務。因此,在伺服器端也必須有相應的套接字與客戶端進行連線。使用如下:

try {
     serverSocket=new ServerSocket(2018);
     socket=serverSocket.accept();
} catch (IOException e) {
     e.printStackTrace();
}

ServerSocket的構造方法中,只有一個引數,即Port號,需要注意的是,伺服器端的Port號必須與客戶端的相同。

當呼叫ServerSocket的accept()方法時,會返回一個客戶端套接字物件,即Socket物件。該物件在客戶端與服務端連線的過程中,將駐留在伺服器的記憶體中,我們通過它就可以與客戶端進行資料的傳輸。

三、客戶端與服務端通訊的簡單實現

客戶端與服務端通訊時,首先服務端要在相應的埠進行監聽,等待客戶端的連線請求。然後在客戶端請求時,接受客戶端的請求,然後進行通訊。首先是服務端。程式碼如下:

package socket.demo2;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServerFrame extends JFrame{
    //接收客戶端傳送的訊息
    private JTextArea allMessage=new JTextArea(8,20);
    // 輸入傳送給客戶端的訊息
    private JTextField sendMessage=new JTextField(20);
    //訊息傳送按鈕
    private JButton sendBtn=new JButton("傳送");
    private JButton startServer=new JButton("開始伺服器");

    private ServerSocket serverSocket=null;//伺服器端Socket
    private BufferedReader in=null;//用於接收客戶端傳送的資料
    private BufferedWriter out=null;//用於傳送資料給客戶端
    private Socket socket=null;//客戶端連線物件

    private int port;
    public void setPort(int port){
        this.port=port;
    }
    public MyServerFrame(){
        init();
    }
    private void init(){
        setTitle("服務端");
        setLayout(new BorderLayout());

        //開始伺服器按鈕單擊事件
        startServer.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    serverSocket=new ServerSocket(port);
                    socket=serverSocket.accept();
                    new receiverMsgThread().start();//啟動執行緒接收客戶端資料
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });
        //傳送按鈕單擊事件
        sendBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                    out.write(sendMessage.getText());
                    out.newLine();
                    out.flush();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }

            }
        });

        //將元件新增到窗體中
        JPanel panel=new JPanel();
        panel.add(startServer);
        panel.add(sendMessage);
        panel.add(sendBtn);

        add(allMessage,BorderLayout.CENTER);
        add(panel,BorderLayout.SOUTH);

        //配置窗體
        setSize(500,300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    //接收資訊執行緒
    class receiverMsgThread extends Thread{
        public void run(){
            try {
                in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String str=null;
                while (true){
                    str=in.readLine()+"\n";
                    allMessage.append("客戶端說:"+str);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyServerFrame myServerFrame=new MyServerFrame();
        myServerFrame.setPort(12345);
    }
}

客戶端程式碼如下:

package socket.demo2;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.Socket;

public class MyClientFrame extends JFrame{
    //接收客戶端傳送的訊息
    private JTextArea allMessage=new JTextArea(8,20);
    // 輸入傳送給客戶端的訊息
    private JTextField sendMessage=new JTextField(20);
    //訊息傳送按鈕
    private JButton sendBtn=new JButton("傳送");
    private JButton connServer=new JButton("連線伺服器");
    private Socket socket=null;
    private BufferedReader in=null;
    private BufferedWriter out=null;

    public MyClientFrame(){
        init();
    }
    private void init(){
        setTitle("客戶端");
        setLayout(new BorderLayout());

        //連線伺服器按鈕單擊事件
        connServer.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    socket=new Socket("127.0.0.1",12345);
                    System.out.println("連線成功");
                    new receiverMsgThread().start();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });
        //傳送按鈕單擊事件
        sendBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                    out.write(sendMessage.getText());
                    out.newLine();
                    out.flush();
                    System.out.println("222");
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });

        //將元件新增到窗體中
        JPanel panel=new JPanel();
        panel.add(connServer);
        panel.add(sendMessage);
        panel.add(sendBtn);

        add(allMessage,BorderLayout.CENTER);
        add(panel,BorderLayout.SOUTH);

        //配置窗體
        setSize(500,300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    class receiverMsgThread extends Thread{
        public void run(){
            InputStream is=null;
            BufferedReader br=null;
            String str=null;
            try {
                is=socket.getInputStream();
                br=new BufferedReader(new InputStreamReader(is));
                while(true){
                    str=br.readLine()+"\n";
                    allMessage.append("伺服器說:"+str);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("客戶端");
        }
    }

    public static void main(String[] args) {
        new MyClientFrame();
    }
}

程式執行介面如下:

目前為止,服務端只能與一個客戶端進行通訊。在編寫的過程中,由於對BufferedReader類,之前並不瞭解,因此在編寫的過程中,遇到了很大的問題,導致走了很多的彎路。最後通過查閱其文件,解決了問題。主要是因為其readLine()方法,在傳送資料時,它是以回車符作為結束的,否則它不會結束,從而導致後續的操作無法完成,因此在傳送資料時,一定要注意,程式碼如下:

out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
out.write(sendMessage.getText());//這個只是將資料寫入到快取中,並不真正進行傳送
out.newLine();//這是一個新行,標識傳輸資料的結束,這點一定要注意
out.flush();//這個是將快取中的資料進行傳送

四、服務端的改進

以上程式程式碼中,伺服器只能接收一個客戶端的連線,下面對伺服器端進行改進,實現多個客戶端的連線。

思想如下:採用多執行緒方式,每一個客戶端連線時,啟動一個執行緒負責該客戶端的通訊,將產生的客戶端Socket放入到一個集合中,然後,當客戶端傳送資料時,伺服器端將收到的資料轉發給所有的客戶端。服務端修改後的程式碼如下;

package socket.demo2;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class MyServerFrame extends JFrame{
    //儲存客戶端的Socket,用於向所有的客戶端進行資料的轉發。
    private static List<Socket> clients=new ArrayList<Socket>();
    //接收客戶端傳送的訊息
    private JTextArea allMessage=new JTextArea(8,20);
    // 輸入傳送給客戶端的訊息
    private JTextField sendMessage=new JTextField(20);
    //訊息傳送按鈕
    private JButton sendBtn=new JButton("傳送");
    private JButton startServer=new JButton("開始伺服器");

    private ServerSocket serverSocket=null;//伺服器端Socket
    private BufferedReader in=null;//用於接收客戶端傳送的資料
    private BufferedWriter out=null;//用於傳送資料給客戶端
    private Socket socket=null;//客戶端連線物件

    private int port;
    public void setPort(int port){
        this.port=port;
    }
    public MyServerFrame(){
        init();
    }
    private void init(){
        setTitle("服務端");
        setLayout(new BorderLayout());

        //開始伺服器按鈕單擊事件
        startServer.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                startServer.setEnabled(false);
                //啟動連線執行緒
                try {
                    serverSocket=new ServerSocket(port);
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
                new connClient().start();
            }
        });
        //傳送按鈕單擊事件
        sendBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    for(Socket s:clients){
                        out=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
                        out.write(sendMessage.getText());
                        out.newLine();
                        out.flush();
                    }
                } catch (IOException e1) {
                    e1.printStackTrace();
                }

            }
        });

        //將元件新增到窗體中
        JPanel panel=new JPanel();
        panel.add(startServer);
        panel.add(sendMessage);
        panel.add(sendBtn);

        add(allMessage,BorderLayout.CENTER);
        add(panel,BorderLayout.SOUTH);

        //配置窗體
        setSize(500,300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    class connClient extends Thread{
        public void run(){
            while(true){
                try {
                    socket=serverSocket.accept();
                    clients.add(socket);
                    new sendMsgToClients(socket).start();
                    new receiverMsgThread().start();//啟動執行緒接收客戶端資料
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    //接收資訊執行緒
    class receiverMsgThread extends Thread{
        public void run(){
            try {
                in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String str=null;
                while (true){
                    str=in.readLine()+"\n";
                    allMessage.append("客戶端說:"+str);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    class sendMsgToClients extends Thread{
        private Socket clientSocket;
        public sendMsgToClients(Socket clientSocket){
            this.clientSocket=clientSocket;
        }
        public void run(){
            //首先接收客戶端傳送過來的資訊,
            //然後向客戶端,進行資訊的轉發。
            try {
                while(true){
                    in=new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                    String str=in.readLine();
                    for(Socket s:clients){
                        out=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
                        out.write(str);
                        out.newLine();
                        out.flush();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                clients.remove(socket);
            }
        }
    }

    public static void main(String[] args) {
        MyServerFrame myServerFrame=new MyServerFrame();
        myServerFrame.setPort(12345);
    }
}