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();
    }
}

程序运行界面如下:

Java Socket网络编程-LMLPHP

Java Socket网络编程-LMLPHP

目前为止,服务端只能与一个客户端进行通信。在编写的过程中,由于对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);
    }
}
12-08 14:14