Java实现简单的Socket服务器与客户端字符串通讯(适合初学者阅读)

来源:互联网 发布:sql查询字段包含字母 编辑:程序博客网 时间:2024/05/22 03:51

       近段时间,频繁看到很多学生做毕业设计用到了Socket通讯技术,问题非常多,特写一个小例子,希望对马上毕业的同学有所帮助。
如果希望学习的更加深入,需要掌握的知识有:面向对象、多线程、Socket通讯、IO流、异常处理

服务器端代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
 * Socket通讯服务器端
 * @author 米强<如转载请保留作者和出处>
 * @blog http://hi.baidu.com/mq612/blog
 * @blog http://blog.csdn.net/mq612
 */
public class ServerMain {
    public ServerMain() {
        try {
            // 构造服务器ServerSocket对象,参数为服务器端开放的端口号
            ServerSocket ss = new ServerSocket(30102);
            System.out.println("服务器准备就绪!");
            // 死循环可以使服务器持续处于接收客户端状态
            while(true){
                // 该方法使程序阻塞,等待客户端的链接,当监听到客户端的链接,创建一个Socket对象与客户端单独会话
                Socket s = ss.accept();
                // 为了不影响服务器监听其它客户端,这里开启了一个线程,由线程处理与这个客户端的会话
                new ServerThread(s).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        new ServerMain();
    }
}

/**
 * 服务器端与客户端会话的线程
 */
class ServerThread extends Thread {
    private Socket s = null;
    private BufferedReader read = null;
    private PrintStream print = null;
    public ServerThread(Socket s) {
        this.s = s;
        try {
            // 从Socket中获取输入流和输出流,由于我们只做一个简单的字符串通讯,所以采用BufferedRead和PrintStream来封装输入、输出流
            read = new BufferedReader(new InputStreamReader(s.getInputStream()));
            print = new PrintStream(s.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 线程的运行run方法
     */
    public void run() {
        try {
            String message = null;
            // 这里循环可以使服务器持续的接收客户端信息。read.readLine()通过输入流读取一段字符串,赋值给message变量,如果message字符串不为“exit”则循环,否则结束循环
            while (!(message = read.readLine()).equals("exit")){
                // 将字符串前面添加“返回:”,再发回客户端
                print.println("返回:" + message);
            }
        } catch (IOException e) {
        } finally {
            // 在 finally 代码块中无论如何都会执行下面代码:
            try {
                // 如果没有关闭Socket
                if(!s.isClosed()){
                    // 关闭Socket链接
                    s.close();
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}

 

客户端代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
/**
 * Socket通讯客户端
 * @author 米强<如转载请保留作者和出处>
 * @blog http://hi.baidu.com/mq612/blog
 * @blog http://blog.csdn.net/mq612
 */
public class ClientMain {
    public ClientMain() {
        try {
            // 构造与服务器通讯的Socket对象,参数为服务器IP地址(String)和端口号(int),端口号需要和服务器端开放的端口号对应
            Socket s = new Socket("192.168.1.100", 30102);
            // 启动一个线程与服务器通讯,并把链接服务器的Socket对象传递过去
            new LinkThread(s).start();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   
   
    public static void main(String[] args) {
        new ClientMain();
    }
   
}
/**
 * 与服务器通讯的线程
 */
class LinkThread extends Thread {
    private Socket s = null;
    // 输出流
    private PrintStream out = null;
    // 缓冲输入流
    private BufferedReader in = null;
    // 录入文字的Scanner对象
    private Scanner scanner = null;
   
    public LinkThread(Socket s) {
        // 将Socket对象实例保存在全局变量中,因为run方法中我们还要用它断开链接
        this.s = s;
        try {
            // 从Socket中获取输入流和输出流,由于我们只做一个简单的字符串通讯,所以采用BufferedRead和PrintStream来封装输入、输出流
            out = new PrintStream(s.getOutputStream());
            in = new BufferedReader(new InputStreamReader(s.getInputStream()));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
   
    /**
     * 线程的运行run方法
     */
    public void run() {
        // 构造Scanner对象
        scanner = new Scanner(System.in);
        System.out.println("提示:如果要结束本次会话,请输入“exit”指令!");
        try {
            // 死循环可以使客户端不断的向服务器发送信息,不用担心循环无法结束,后面的return语句可以结束整个线程。
            while(true){
                // 提示用户输入文字
                System.out.print("请输入:");
                // 将用户输入的字符串保存在message变量中
                String message = scanner.nextLine();
                // 通过输出流发送字符串
                out.println(message);
                // 清空缓冲,强制输出
                out.flush();
                // 获取服务器返回的字符串
                String str = in.readLine();
                // 如果返回的字符串存在
                if(str != null){
                    // 显示在控制台
                    System.out.println(str);
                }else{
                    // 提示会话结束,并结束线程
                    System.out.println("本次会话结束!");
                    return;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 在 finally 代码块中无论如何都会执行下面代码:
            try {
                // 如果没有关闭Socket
                if(!s.isClosed()){
                    // 关闭Socket链接
                    s.close();
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
   
}