java中的socket

此次在java实验的时候,要求使用server socket编写服务器和客户端的网络通讯。最开始认为应该是挺简单的,可是后来发现低估了它。出现了很多的问题,因此也在这里与你们分享。java

问题描述

服务器程序的处理规则以下:
1) 向客户端程序发送Verifying Server!。
2) 若读口令次数超过3次,则发送Illegal User!给客户端,程序退出。不然向下执行步骤3)。
3) 读取客户端程序提供的口令。
4) 若口令不正确,则发送PassWord Wrong!给客户端,并转步骤2),不然向下执行步骤5)。
5) 发送Registration Successful!给客户端程序。

客户端程序的处理规则以下:
1) 读取服务器反馈信息。
2) 若反馈信息不是Verifying Server!,则提示Server Wrong!,程序退出。不然向下执行步骤3)
3) 提示输入PassWord并将输入的口令发送给服务器。
4) 读取服务器反馈信息。
5) 若反馈信息是Illegal User!,则提示Illegal User!,程序退出。不然向下执行步骤6)
6) 若反馈信息是PassWord Wrong!,则提示PassWord Wrong!,并转步骤3),不然向下执行步骤。
7) 输出Registration Successful!。服务器

初步实现

首先,咱们必定要清楚,此次和以前的程序不一样,虽然都是在本地上,可是服务器客户端须要两个启动程序来实现,以达到咱们模拟远程链接的效果。网络

而后就是如何利用socket实现咱们的功能了。多线程

clipboard.png

经过上面的图示,咱们能够知道,首先须要先开启服务器,而后等待客户端的链接。socket

当客户端经过socket进行链接后,服务器端也会创建一个socket对象来帮助实现服务器和客户端的通讯。ide

这时候就创建了一个TCP链接,咱们就能够在服务器写数据,而后在客户端读取了,实现双方通讯。函数

最后,当有一方决定通讯结束,就会关闭链接。通讯结束。this

下面是初步实现的代码:spa

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服务器
 */
public class ServerTest {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            Socket clientSocket = serverSocket.accept();

            String welcome  = "verifying server!";
            OutputStream outputStream = clientSocket.getOutputStream();
            outputStream.write(welcome.getBytes());

            InputStream inputStream = clientSocket.getInputStream();
            int time = 0;

            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String password = bufferedReader.readLine();

            // 获取登陆信息,容许3次登陆
            while (time < 3) {
                if (password.equals("123")) {
                    outputStream.flush();
                    outputStream.write("Registration Successful!".getBytes());
                    break;
                } else {
                    outputStream.write("PassWord Wrong!".getBytes());
                    outputStream.flush();
                    password = bufferedReader.readLine();
                    time++;
                }
            }

            if (time >= 3) {
                outputStream.flush();
                outputStream.write("Illegal User!".getBytes());
            }

            outputStream.close();
            clientSocket.close();
            serverSocket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.*;
import java.net.Socket;

/**
 * 客户端
 */
public class ClientTest {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 8080);

            String aline = new String();
            
            // 获取输入流
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            aline = bufferedReader.readLine();

            // 访问失败
            if (!aline.equals("verifying server!")) {
                System.out.println("Server Wrong!");
                return;
            }

            // 获取响应结果
            String feedback = bufferedReader.readLine();

            while (feedback == null || feedback.equals("PassWord Wrong!")) {
                if (feedback != null) {
                    if (feedback.equals("PassWord Wrong!")) {
                        System.out.println("PassWord Wrong!");
                    } else if (feedback.equals("Registration Successful!")) {
                        System.out.println("Registration Successful!");
                        break;
                    }
                }

                System.out.println("输入密码:");
                
                // 输入密码
                Scanner scanner = new Scanner(System.in);
                OutputStream outputStream = socket.getOutputStream();
                String password = scanner.nextLine();
                outputStream.write(password.getBytes());
                
                feedback = bufferedReader.readLine();
            }

            // 关闭链接
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

初步实现后,运行:一片空白。什么都没有发生。.net

出问题了很正常,断点调试一下吧。发现问题所在:

clipboard.png

客户端执行到这一行时,中止,而后服务器端接着执行;

clipboard.png

一样的服务器端也到这行就中止了。

缘由是服务器一方等着输入密码,而客户端一方等着响应结果,而后又没有开始输入密码。因此双方就这么一直等着,谁都不动了。

解决

经过上面的分析,咱们只要将僵局打破就可以解决问题。因此就先输入密码,而后再获取响应结果。这样就不会出问题了。

因此服务器端的代码并不须要作什么改动,只用修改客户端程序就好了。

import java.io.*;
import java.net.Socket;

/**
 * 客户端
 */
public class ClientTest {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 8080);

            String aline = new String();
            
            // 获取输入流
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            aline = bufferedReader.readLine();

            // 访问失败
            if (!aline.equals("verifying server!")) {
                System.out.println("Server Wrong!");
                return;
            }

            // 主要修改这里,不先获取响应结果
            // 初始化响应结果
            String feedback = null;

            while (true) {
                if (feedback != null) {
                    if (feedback.equals("PassWord Wrong!")) {
                        System.out.println("PassWord Wrong!");
                    } else if (feedback.equals("Registration Successful!")) {
                        System.out.println("Registration Successful!");
                        break;
                    }
                }

                System.out.println("输入密码:");
                
                // 输入密码
                Scanner scanner = new Scanner(System.in);
                OutputStream outputStream = socket.getOutputStream();
                String password = scanner.nextLine();
                outputStream.write(password.getBytes());
                
                feedback = bufferedReader.readLine();
            }

            // 关闭链接
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

clipboard.png

成功实现!


升级:利用多线程实现

功能成功实现以后,还不能知足于当前的情况。因为在实际使用socket的时候,不少时候都不是简单的一对一的状况,这种状况下,就须要使用多线程来帮助咱们了。

使用多线程,咱们能够实现一个服务器多个客户端的状况,每当有一个客户端请求链接,咱们就会创建一个线程。

1.服务器端线程实现

首先将服务器独立成一个线程:

/**
 * 服务器线程
 */
public class ServerThread extends Thread {
    private ServerSocket serverSocket;
    private Socket clientSocket;

    public ServerThread(int port) {
        try {
            serverSocket = new ServerSocket(port);
            // 接受客户端链接请求
            clientSocket = serverSocket.accept();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在构造函数中咱们初始化服务器的socket,而后等待客户端的链接。接着就是最主要的部分了,线程主要执行的逻辑功能:run

@Override
public void run() {
    try {
        // 提示链接成功
        DataOutputStream dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());
        dataOutputStream.writeUTF("verifying server!");

        // 获取输入流
        InputStream inputStream = clientSocket.getInputStream();
        DataInputStream dataInputStream = new DataInputStream(inputStream);
        String password = dataInputStream.readUTF();

        // 获取登陆信息,容许3次登陆
        int time = 0;

        // 密码校验,容许输入3次
        while (time < 3) {
            if (password.equals("123")) {
                dataOutputStream.writeUTF("Registration Successful!");
                break;
            } else {
                dataOutputStream.writeUTF("PassWord Wrong!");
                password = dataInputStream.readUTF();
                time++;
            }
        }

        if (time >= 3) {
            dataOutputStream.writeUTF("Illegal User!");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

run没有什么能够说的,主要就是实现服务器与客户端交互的时候执行的功能。而后在执行线程的时候,会自动调用run方法。

最后就是启动服务器端了:

/**
 * 服务器端
 */
public class ServiceTest {
    public static void main(String[] args) {
        ServerThread serverThread = new ServerThread(8080);
        serverThread.start();
    }
}

一样,启用8080端口。

2.客户端线程实现

相似服务器端,首先先创建客户端线程:

/**
 * 客户端线程
 */
public class ClientThread extends Thread {
    private Socket socket;

    public ClientThread(String host, int port) {
        try {
            this.socket = new Socket(host, port);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在构造函数中,创建客户端的socket对象。而后实现run方法。

@Override
public void run() {
    try {
        // 获取输入流
        DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
        String aline = dataInputStream.readUTF();

        // 链接服务器失败
        if (!aline.equals("verifying server!")) {
            System.out.println("Server Wrong!");
            return;
        }

        // 获取响应结果
        String feedback = null;

        // 进行密码输入
        while (true) {
            if (feedback != null) {
                if (feedback.equals("PassWord Wrong!")) {
                    System.out.println("PassWord Wrong!");
                } else if (feedback.equals("Registration Successful!")) {
                    System.out.println("Registration Successful!");
                    System.exit(1);
                }
            }

            System.out.println("输入密码:");
            Scanner scanner = new Scanner(System.in);
            DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
            String password = scanner.nextLine();
            dataOutputStream.writeUTF(password);
            
            // 获取响应结果
            feedback = dataInputStream.readUTF();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

run方法中,主要实现了输入密码的功能。

最后就是启动客户端线程:

/**
 * 客户端
 */
public class ClientTest {
    public static void main(String[] args) {
        ClientThread clientThread = new ClientThread("127.0.0.1", 8080);
        clientThread.start();
    }
}

总结

此次的实验给我提了个醒,看似简单的东西,也永远不要轻视它。只有拿出应有的实例去解决问题,问题才是你眼中那个简单的问题。

相关文章
相关标签/搜索