GitHub B站UP主:sBobHuang

TCP/IP 协议栈学习代码

全部代码

直接使用socket

客户端

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

public class Client {
     public static void main(String[] args)throws IOException {
        Socket socket=new Socket();
        //超时时间
        socket.setSoTimeout(3000);
        //连接本地端口8080,超时时间2000ms
         socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(),8080),3000);
         System.out.println("已发起服务器连接并进入后续流程~");
         System.out.println("客户端信息:"+socket.getLocalAddress()+" P:"+socket.getLocalPort());
         System.out.println("服务端信息:"+socket.getInetAddress()+" P:"+socket.getPort());
         try{
             //发送接受数据
             todo(socket);
         }catch (Exception e){
             System.out.println("异常关闭");
         }
         socket.close();
         System.out.println("客户端已退出~");

    }
    private static  void todo(Socket client) throws IOException{
         //构建键盘输入流
        InputStream in=System.in;
        BufferedReader input=new BufferedReader(new InputStreamReader(in));

        //socket输出流,并转换为打印流
        OutputStream outputStream=client.getOutputStream();
        PrintStream socketPrintStream=new PrintStream(outputStream);

        //得到Socket输入流,并转换为BufferReader
        InputStream inputStream=client.getInputStream();
        BufferedReader socketBufferedReader=new BufferedReader(new InputStreamReader(inputStream));

        boolean flag=true;
        do {
            //键盘读取一行
            String str =input.readLine();

            //发送到服务器
            socketPrintStream.println(str);

            //从服务器读取一行
            String echo = socketBufferedReader.readLine();
            if ("bye".equalsIgnoreCase(echo)) {
                flag = false;
            }
            else
            {
                System.out.println(echo);
            }
        }
        while (flag);

        //资源释放
        socketPrintStream.close();
        socketBufferedReader.close();
    }
}

服务端

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket server=new ServerSocket(8080);


        System.out.println("服务器准备就绪~");
        System.out.println("服务器信息:"+server.getInetAddress()+" P:"+server.getLocalPort());


        for( ;;) {
            //等待客服端连接
            Socket client = server.accept();
            ClientHandler clientHandler = new ClientHandler(client);
            clientHandler.start();
        }
    }
    /*
    客户端消息处理
     */
    private  static class ClientHandler extends Thread{
        private Socket socket;
        private boolean flag=true;
        ClientHandler(Socket socket)
        {
            this.socket=socket;

        }

        @Override
        public void run() {
            super.run();
            System.out.println("新客户端连接:"+socket.getInetAddress()+" P:"+socket.getLocalPort());

            try{
                //得到打印流,用于数据输出;服务器回送数据
                PrintStream socketOutput=new PrintStream(socket.getOutputStream());
                //得到输入流,用于接受数据
                BufferedReader socketInput =new BufferedReader(new InputStreamReader(
                        socket.getInputStream()));
                do {
                    //客户端拿到一条数据
                    String str=socketInput.readLine();
                    if("bye".equalsIgnoreCase(str))
                    {
                        flag=false;
                        //回送
                        socketOutput.println("bye");
                    }
                    else
                    {
                        System.out.println(str);
                        socketOutput.println("回送:"+str.length());
                    }
                }while(flag);
                socketInput.close();
                socketOutput.close();
            }catch (Exception e) {
                System.out.println("连接异常断开");
            }finally {
                //连接关闭
                try {
                socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("客户端已关闭"+socket.getInetAddress()+" P:"+socket.getLocalPort());
        }
    }
}

使用UDP

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/*
UDP提供者,用于提供服务
 */
public class UDPProvider {
    public static void main(String[] args) throws IOException {
        System.out.println("UDPProvider Started.");
        //作为接收者,指定一个端口用于数据接收
        DatagramSocket ds=new DatagramSocket(20000);

        //构建接收实体
        final byte[] buf=new byte[512];

        DatagramPacket receivePack=new DatagramPacket(buf,buf.length);

        //接收
        ds.receive(receivePack);

        //打印接收到的信息与发送者的信息
        //发送者的IP地址
        String ip=receivePack.getAddress().getHostAddress();
        int port=receivePack.getPort();
        int dataLen=receivePack.getLength();
        String data=new String(receivePack.getData(),0,dataLen);
        System.out.println("UDPProvider receive form IP: +ip" +
                "\tPort"+port +"\tData:"+data);
        //构建一份回送数据
        String responseData="Receive data with len"+dataLen;
        byte[] responseDataBytes=responseData.getBytes();
        //直接根据发送者构建一份回送信息
        DatagramPacket respnsePacket= new DatagramPacket(responseDataBytes,
                responseDataBytes.length,
                receivePack.getAddress(),
                receivePack.getPort()
        );
        ds.send(respnsePacket);
        //完成
        System.out.println("UDPProvider Finished.");
        ds.close();

    }
}

 

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


/*
UDP提供者,用于提供服务
 */
public class UDPSearcher {
    public static void main(String[] args) throws IOException {
        System.out.println("UDPSearcher Started.");
        //作为搜素者,无需指定端口
        DatagramSocket ds=new DatagramSocket();
        //构建一份请求数据
        String resquestData="Hello world!";
        byte[] resquestDataBytes=resquestData.getBytes();
        DatagramPacket resquestPacket= new DatagramPacket(resquestDataBytes,
                resquestDataBytes.length
        );
        //本机20000端口
        resquestPacket.setAddress(InetAddress.getLocalHost());
        resquestPacket.setPort(20000);
        //发送
        ds.send(resquestPacket);
        //构建接收实体
        final byte[] buf=new byte[512];

        DatagramPacket receivePack=new DatagramPacket(buf,buf.length);

        //接收
        ds.receive(receivePack);

        //打印接收到的信息与发送者的信息
        //发送者的IP地址
        String ip=receivePack.getAddress().getHostAddress();
        int port=receivePack.getPort();
        int dataLen=receivePack.getLength();
        String data=new String(receivePack.getData(),0,dataLen);
        System.out.println("UDPSearcher receive form IP: +ip" +
                "\tport"+"Port:"+port +"\tdata:"+data);

        //完成
        System.out.println("UDPSearcher Finished.");
        ds.close();

    }
}

使用SN

public class MessageCreator {
    private static final String SN_HEADER="收到暗号,我是(SN):";
    private static final String PORT_HEADER="这是暗号,请回电口(Port)";

    public static String buildWithPort(int port){
        return PORT_HEADER+port;
    }
    public static int parsePort(String data){
        if(data.startsWith(PORT_HEADER)){
            return Integer.parseInt(data.substring(PORT_HEADER.length()));
        }
        return -1;
    }

    public static String buildWithSn(String sn){
        return SN_HEADER+sn;
    }
    public static String parseSN(String data){
        if(data.startsWith(SN_HEADER)){
            return data.substring(SN_HEADER.length());
        }
        return null;
    }
}

 根据上面写的能运用的局域网搜索

public class MessageCreator {
    private static final String SN_HEADER="收到暗号,我是(SN):";
    private static final String PORT_HEADER="这是暗号,请回电。口(Port)";

    public static String buildWithPort(int port){
        return PORT_HEADER+port;
    }
    public static int parsePort(String data){
        if(data.startsWith(PORT_HEADER)){
            return Integer.parseInt(data.substring(PORT_HEADER.length()));
        }
        return -1;
    }

    public static String buildWithSn(String sn){
        return SN_HEADER+sn;
    }
    public static String parseSN(String data){
        if(data.startsWith(SN_HEADER)){
            return data.substring(SN_HEADER.length());
        }
        return null;
    }
}

 

import java.io.IOException;
import java.net.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;


/*
UDP提供者,用于提供服务
 */
public class UDPSearcher {
    private static final int LISTEN_PORT=30000;
    public static void main(String[] args) throws IOException, InterruptedException {
        System.out.println("UDPSearcher Started.");
        Listener listener=listen();
        sendBroadCast();
        //读取任意键盘信息可以退出
        System.in.read();
        List<Device> devices=listener.getDevicesAndClose();
        for(Device device:devices){
            System.out.println("Device"+device.toString());
        }
        //完成
        System.out.println("UDPSearcher Finished");

    }
    private static Listener listen() throws InterruptedException {
        System.out.println("UDPSearcher start listen.");
        CountDownLatch countDownLatch=new CountDownLatch(1);
        Listener listener=new Listener(LISTEN_PORT,countDownLatch);
        listener.start();

        countDownLatch.await();
        return listener;
    }
    private static void sendBroadCast() throws IOException {
        System.out.println("UDPSearcher sendBroadCast Started.");
        //作为搜素者,无需指定端口
        DatagramSocket ds=new DatagramSocket();
        //构建一份请求数据
        String resquestData=MessageCreator.buildWithPort(LISTEN_PORT);
        byte[] resquestDataBytes=resquestData.getBytes();
        DatagramPacket resquestPacket= new DatagramPacket(resquestDataBytes,
                resquestDataBytes.length
        );
        //本机20000端口
        resquestPacket.setAddress(InetAddress.getByName("255.255.255.255"));
        resquestPacket.setPort(20000);
        //发送
        ds.send(resquestPacket);
        ds.close();

        //完成
        System.out.println("UDPSearcher sendBroadCast Finished.");
    }
    private static class Device{
        final int Port;
        final String ip;
        final String sn;

        public Device(int port, String ip, String sn) {
            Port = port;
            this.ip = ip;
            this.sn = sn;
        }

        @Override
        public String toString() {
            return "Device{" +
                    "Port=" + Port +
                    ", ip='" + ip + '\'' +
                    ", sn='" + sn + '\'' +
                    '}';
        }
    }
    private static class Listener extends Thread{
        private final int listenPort;
        private final CountDownLatch countDownLatch;
        private final List<Device> devices=new ArrayList<>();
        private boolean done=false;
        private DatagramSocket ds=null;
        public Listener(int listenPort, CountDownLatch countDownLatch){
            super();
            this.listenPort = listenPort;
            this.countDownLatch = countDownLatch;
        }
        @Override
        public void run(){
            super.run();
            System.out.println("UDPSearcher started");
            //通知已启动
            countDownLatch.countDown();
            try{
                //监听端口
                ds=new DatagramSocket(listenPort);
                while(!done){

                    //构建接受实体
                    final byte[] buf=new byte[512];

                    DatagramPacket receivePack=new DatagramPacket(buf,buf.length);

                    //接收
                    ds.receive(receivePack);

                    //打印接收到的信息与发送者的信息
                    //发送者的IP地址
                    String ip=receivePack.getAddress().getHostAddress();
                    int port=receivePack.getPort();
                    int dataLen=receivePack.getLength();
                    String data=new String(receivePack.getData(),0,dataLen);
                    System.out.println("UDPProvider receive from IP:" +ip +
                            "\tPort"+port +"\tData:"+data);

                    String sn=MessageCreator.parseSN(data);
                    if(sn!=null){
                        Device device=new Device(port,ip,sn);
                        devices.add(device);
                    }

                }
            }catch (Exception ignored) {

            }finally {
                close();
            }
            System.out.println("UDPProvider Searcher stoped");
        }
        private void close(){
            if(ds!=null){
                ds.close();
                ds=null;
            }
        }
        List<Device>getDevicesAndClose(){
            done=true;
            close();
            return devices;
        }

    }

}

 

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.UUID;

/*
UDP提供者,用于提供服务
 */
public class UDPProvider {
    public static void main(String[] args) throws IOException {
        //生成唯一标示
        String sn= UUID.randomUUID().toString();
        //构建线程,启动线程
        Provider provider=new Provider(sn);
        provider.start();
        //读取键盘信息后可以退出
        System.in.read();
        provider.exit();
    }
    private static class Provider extends Thread{
        private final String sn;
        private boolean done=false;
        private DatagramSocket ds=null;
        public Provider(String sn){
            super();
            this.sn=sn;
        }

        @Override
        public void run() {
            super.run();
            System.out.println("UDPProvider Started.");
            try {
                //监听20000端口
                ds = new DatagramSocket(20000);
                while (!done) {
                    //构建接收实体
                    final byte[] buf = new byte[512];

                    DatagramPacket receivePack = new DatagramPacket(buf, buf.length);
                    //接收
                    ds.receive(receivePack);
                    //打印接收到的信息与发送者的信息
                    //发送者的IP地址
                    String ip = receivePack.getAddress().getHostAddress();
                    int port = receivePack.getPort();
                    int dataLen = receivePack.getLength();
                    String data = new String(receivePack.getData(), 0, dataLen);
                    System.out.println("UDPProvider receive from IP:" +ip +
                            "\tPort" + port + "\tData:" + data);
                    //解析端口号
                    int responsePort=MessageCreator.parsePort(data);
                    if(responsePort!=-1) {
                        //构建一份回送数据
                        String responseData = MessageCreator.buildWithSn(sn);
                        byte[] responseDataBytes = responseData.getBytes();
                        //直接根据发送者构建一份回送信息
                        DatagramPacket responsePacket = new DatagramPacket(responseDataBytes,
                                responseDataBytes.length,
                                receivePack.getAddress(),
                                responsePort
                        );
                        ds.send(responsePacket);
                    }
                }
            } catch (Exception ignored) {

            } finally {
                close();
            }
        }

        private void close(){
            if(ds!=null) {
                ds.close();
                ds=null;
            }
        }

        void exit(){
            done=true;
            close();
        }
    }
}

 服务器可以做HTTP的代理

2MSL
        当TCP执行主动关闭,并发出最后一个ACK,该链接必须在TIME_WAIT状态下停留的时间为2MSL。这样可以(1)让TCP再次发送最后的ACK以防这个ACK丢失(被动关闭的一方超时并重发最后的FIN);保证TCP的可靠的全双工连接的终止。(2)允许老的重复分节在网络中消失。参考文章《unix网络编程》(3)TCP连接的建立和终止 在TIME_WAIT状态 时两端的端口不能使用,要等到2MSL时间结束才可继续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。不过在实际应用中可以通过设置 SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。

posted @ 2019-07-19 12:18  暴力都不会的蒟蒻  阅读(...)  评论(... 编辑 收藏
TOJ