基于TCP协议的Socket通信 JAVA多线程版(详细注释)

TCP连接流程

TCP socket连接
整个的连接流程如图所示,需要注意的是服务器端创建了两个socket,一个用于监听,一个用于接受具体的连接,而本文所主要采用的多线程客户-服务器socket通信,主要是将连接成功的socket交给不同的线程去处理,来执行不同的数据传输任务(图中蓝色部分),从而服务器可以继续监听来自其他客户端的连接不被阻塞住。

代码实战

1. 目录结构

TCPJAVA

2. 代码详解

客户端代码 Client.java
package myTCP;
import java.net.Socket;
import java.net.UnknownHostException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
public class Client {    
    public static void main(String[] args) {        
        try{    
                // 创建 socket 与 localhost:8888(服务器) 建立连接
                Socket socket = new Socket("localhost", 8888);
                

                //注意以上连接方式和流程图不符
                //我们没有绑定客户端的本地端口,如果我们想绑定可以按照如下方式:
                //Socket socket=new Socket();
                //socket.bind(new InetSocketAddress(3000)); // 绑定了本地的3000端口
                //socket.connect(new InetSocketAddress("localhost", 8888));

                // 获取socket输出流(这里的输出是相对与客户端的也就是会输出到服务器)
                OutputStream outStream = socket.getOutputStream();
                // PrintWriter 以便以我们可以传入更多类型的数据
                PrintWriter printWriter = new PrintWriter(outStream);
                // 要传输的内容
                printWriter.println("userName: zxd passWord: zxd");
                // 刷新缓冲区, 以免缓冲区残留数据
                printWriter.flush();
                // 关闭输出流,代表客户端向服务器传输数据结束
                socket.shutdownOutput();
                
                // 获取socket输入流(这里的输出是相对与客户端的也就是会会接受服务器的输出)
                InputStream is = socket.getInputStream();
                // InputStreamReader 使得我们可以按不同编码方式读取
                InputStreamReader isr = new InputStreamReader(is);
                // 读缓冲区
                BufferedReader br = new BufferedReader(isr);
                // 定义接收数据格式
                String data = null;
                // 按行接收并打印结果
                while ((data = br.readLine()) != null) {
                    System.out.println("服务器返回的数据为:"+data);
                }            
                // 收发数据均已经结束 关闭socket
                socket.close();
            // 以下是必须进行捕获的异常
            } catch (UnknownHostException e){
                e.printStackTrace();        
            } catch (IOException e) {
                e.printStackTrace();        
            }    
    }
}
服务端代码 Server.java
package myTCP;
import java.net.ServerSocket;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
public class Server {    
    public static void main(String[] args) {
        try {
            // 定义监听socket 监听8888端口
            ServerSocket serverSocket =  new ServerSocket(8888);
            // 定义连接socket
            Socket socket = null;
            // 记录客户端的连接次数
            int count = 0;
            // 此时服务器已经准备好 打印log
            System.out.println("Sever Start!!");
            // 循环监听
            while (true) {
                // 连接socket建立成功, 
                socket = serverSocket.accept();
                // 将具体的数据传输任务丢给连接处理线程 
                Thread thread = new Thread(new ServerThread(socket));
                // 线程开始运行
                thread.start();
                // 连接数加一
                count++;
                // 实时打印log 显示连接数
                System.out.println("The num of connection :" + count);
                // 获取连接地址
                InetAddress address = socket.getInetAddress();
                // 实时打印log 显示当前连接的客户端地址
                System.out.println("The address of client now:" + 
                address.getHostAddress());
            }
        // 服务器的连接不用关闭 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
服务端多线程代码 ServerThread.java
package myTCP;
import java.net.Socket;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.IOException;
//  线程类实现runnable接口
public class ServerThread implements Runnable{
    // 对应连接socket
    Socket socket = null;
    // 构造函数初始化连接socket
    public ServerThread(Socket socket){
        this.socket = socket;
    }
    public void run(){
        // 与客户端一样定义一系列输入输出流和缓冲区
        InputStream is = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        OutputStream os = null;
        PrintWriter pw = null;
        try{
            // 获取客户端的输入
            is = socket.getInputStream();
            isr = new InputStreamReader(is);
            br = new BufferedReader(isr);
            // 定义接收数据
            String data = null;
            
            // 按行接收,并返回数据给客户端
            while((data = br.readLine()) != null) {
                System.out.println("The client seed data:" + data);
                os = socket.getOutputStream();
                pw = new PrintWriter(os);
                pw.println("Access tcpServer Successfully!");
                pw.flush();
            }
        // 必须进行的异常捕获
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 按顺序关闭缓冲区和流(先开后关)
            try {
                if (pw != null) {
                pw.close();
                }
                if (os != null) {
                os.close();
                }
                if (br != null) {
                br.close();
                }
                if (isr != null) {
                isr.close();
                }
                if (is != null) {
                is.close();
                }
                if (socket!= null) {
                socket.close();
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

3. 结果展示

  • 先运行Server.java 文件
    服务器运行

  • 在运行客户端文件
    客户端运行

  • 此时服务端会打印log
    服务器

  • 保持服务端开启,再次运行客户端程序
    服务器

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

从前慢慢慢死了

打钱!一分也行啊!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值