全部代码
直接使用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时间结束再使用此端口。