TCP编程

背景

进行网络编程,就需要传输层为应用层提供的api,称为socket api,其中传输层提供了两种协议,TCP和UDP,其所对应的socket api也是不同的,下面介绍的是TCP协议,前面写过UDP,下面有些与UDP类似的操作不再细说。

1.特点

1.有连接(像电话一样需要先建立连接后才能通信)
2.可靠传输(就像钉钉一样,发送消息,若对方看见了则会显示已读)
3.面向字节流(以字节为传输的基本单位)
4.全双工(支持双向通信,可以在A给B通信的同时,B给A通信)

2.核心类

1.ServerSocket:(服务器端使用的socket)
2.Socket:(服务器和客户端都会使用的socket)

3.回显服务器的实现

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    private ServerSocket serverSocket = null;

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        ExecutorService service = Executors.newCachedThreadPool();//此处使用自动扩容的线程池,因为不知道客户端的数量

        while (true) {
            Socket clientSocket = serverSocket.accept();// 连接是系统内部做的事情,连接建立好以后将连接拿到应用程序里面,若连接还没有建立,就会阻塞,相当于别人给你打电话的接电话操作
           // processConnect(clientSocket);//
//            Thread thread = new Thread(()->{
//                try {
//                    processConnect(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            thread.start();//如果有好多客户端去访问会频繁的创建销毁线程,故使用线程池代替
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                    processConnect(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                }
            });
        }
    }

    //通过这个方法给连上的客户端提供服务
    //服务方式有两种,短连接:一个请求和一个响应。长连接:多个请求和多个响应(多次数据交互)
    //此处是长连接版本
    public void processConnect(Socket clientSocket) throws IOException {
        System.out.println("建立连接  " + " IP:" + clientSocket.getInetAddress().toString() + " 端口 " + clientSocket.getPort());
        try (InputStream inputStream = clientSocket.getInputStream();//获取Socket内部的输入流对象,用来接收数据
             OutputStream outputStream = clientSocket.getOutputStream()) {//发送数据
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            //用循环来获取多次交互的情况
            while (true) {
                if (!scanner.hasNext()) {
                    //连接断开,读完了的时候
                    System.out.println("断开连接  " + " IP:" + clientSocket.getInetAddress().toString() + " 端口 " + clientSocket.getPort());

                    break;
                }
                //1.读取请求
                String request = scanner.next();
                //2.根据请求计算响应
                String response = process(request);
                //3.把响应写回客户端
                printWriter.println(response);
                printWriter.flush();//刷新缓冲区,避免因为数据在缓冲区而没有发送到客户端
                System.out.println("请求  " + request + "响应 " + response + "IP:" + clientSocket.getInetAddress().toString() + " 端口 " + clientSocket.getPort());

            }
        }finally {
            clientSocket.close();
        }

    }

    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(6888);
        tcpEchoServer.start();
    }
}


4.客户端的实现

import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoClient {
    private Socket socket = null;
    public TcpEchoClient() throws IOException {
        //new对象是需要与服务器建立连接的,就需要服务器在哪里
        socket = new Socket("127.0.0.1",6888);

    }
    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        //长连接:一个连接会处理n个请求和响应
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()){
            Scanner scanner1 = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while(true){
                //1.从控制台读入用户输入
                String request = scanner.next();
                //2.把请求发送给服务器
                printWriter.println(request);
                printWriter.flush();
                //3.从服务器读取响应
                String response = scanner1.next();
                //4.把结果显示到界面上
                System.out.println("req:"+request+"  response:"+response);
            }
        }

    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient tcpEchoClient = new TcpEchoClient();
        tcpEchoClient.start();
    }
}


5.注意事项

1.在输入请求的时候,scanner.next()会读取你的空白符,并且不会保留这个空白符而返回给接收的变量,而这个函数是读到空白符停止。
2.使用多线程解决无法多个客户端访问服务器的问题
3.使用线程池解决频繁创建和销毁线程的问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值