udp
DatagramSocket
用于接收和发送udp数据报
构造方法:
- DatagramSocket():创建一个UDP数据报套接字的Socket,绑定到本地上 一个随机可用端口上,一般用于客户端
- DatagramSocket(int port):创建一个UDP数据报套接字的Socket,绑定到指定端口上,一般适用于服务器端
DatagramSocket方法:
- void receive(DatagramPacket p):接收套接字数据报p,如果此时还没有发送,便会阻塞
- void send(DatagramPacket p):发送套接字数据报p,不会阻塞,直接发送
- void close():关闭此数据报套接字
DatagramPacket:
udp发送和接收的数据报形式
构造方法:
- DatagramPacket(byte[] buf,int length):构造一个DatagramPacket对象来接收发送来到数据报,将数据内容放在第一个字节数组中,第二个参数是指定接收内容的长度。(用于接收时)
- DatagramPacket(byte[] buf,int offset,int length,SocketAddress address):接收发来的数据报,并将内容放进buf数组中,选中需要截取内容的索引[offset,length)。address:指定目的主机的ip和端口号。(用于发送时)
DatagramPacket方法:
- getSocketAddress():获取发送端的ip地址。
- getPort():获取发送端的端口
- byte[] getData():获取数据报中的内容
udp回显服务器实现:
服务器端:
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.nio.charset.StandardCharsets;public class udpServer {//服务器端private DatagramSocket socket = null;public udpServer(int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {//System.out.println(socket.getPort());//returns the port to which the socket is connected 未连接套接字时 返回-1//如果想得到服务器端的端口 要使用socket.getLocalPort()System.out.println("服务器启动");//构建一个DatagramPacket来接收客户端发来的消息while(true){//创建一个DatagramPacket填充对象,用来接收从客户端发送来的数据报DatagramPacket reqPacket = new DatagramPacket(new byte[4096],4096);//如果没有数据报发送,receive发送会阻塞等待socket.receive(reqPacket);//转换成我们看得懂的字符串形式 ----- 客户端发送来的请求String request = new String(reqPacket.getData(),0,reqPacket.getLength());//服务器端做出响应String res = RESPONSE(request);//构建一个DatagramPacket将响应后的结果发送回客户端//注意需要有客户端socketAddressDatagramPacket respPacket = new DatagramPacket(res.getBytes(StandardCharsets.UTF_8),0,res.getBytes().length,reqPacket.getSocketAddress());//发送数据报socket.send(respPacket);System.out.println("服务器端口:"+socket.getLocalPort()+": 客户端[ip:"+respPacket.getAddress()+" 端口:"+reqPacket.getPort()+"] req:"+request+" res:"+res);}}public String RESPONSE(String req){return req;}public static void main(String[] args) throws IOException {udpServer server = new udpServer(8080);server.start();} }
值得注意的是:DatagramSocket的getPort/getAddress方法,返回的都是连接方的端口和ip,并不是本地的。而DatagreamPacket的getPort/getAddress方法,返回的都是本地的端口和ip地址。如果你还没有连接成功时,打印socket.getPort(),虽然这时候你已经给出了指定端口,但这个方法的返回值是连接端的,还没连接时返回-1
客户端
import java.io.IOException; import java.net.*; import java.nio.charset.StandardCharsets; import java.util.Scanner;public class udpClient {//目的端ipprivate String severIp;//目的端端口private int severPort;private DatagramSocket socket;public udpClient(String severIp,int severPort) throws SocketException {socket = new DatagramSocket();this.severIp = severIp;this.severPort = severPort;}public void start() throws IOException {Scanner sc = new Scanner(System.in);System.out.println("客户端启动");while(true){System.out.print(">>");String request = sc.nextLine();if(request.equals("exit")){System.out.println("退出");socket.close();return;}//给出消息发出的目的端ip和端口 (服务器ip端口)DatagramPacket reqPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8),request.getBytes().length,InetAddress.getByName(severIp),severPort);//发送socket.send(reqPacket);//接收响应DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);//转换成字符串形式String res = new String(responsePacket.getData(),0, responsePacket.getLength());//打印响应System.out.println(res);}}public static void main(String[] args) throws IOException {udpClient client = new udpClient("127.0.0.1",8080);client.start();} }
注意:当传服务器端的ip地址时,此时我们这里给的是一个字符串,但参数接收的是一个32位整数形式,所以我们需使用InetAddress.getName(String hostage)做一下转换
tcp
SeverSocket
服务器端Socket API
构造方法:
- SeverSccket(int port):创建一个服务器端流套接字Socket,并绑定到指定端口
SeverSocket的方法:
- Socket accept():监听指定端口(创建时绑定的端口),如果有客户端连接后,返回一个服务端Socket对象,否则阻塞等待
- void close():关闭套接字
Socket
客户端Socket,或服务器端的accept方法收到有客户端连接后返回的服务端Socket
无论是客户端还是服务器端,都是连接建立后,保存对端的信息
构造方法:
Socket(String severIp,int severPort):创建一个客户端Socket,与对应的主机端口建立连接
Socket的方法:
- InetAddress():返回套接字所连接的地址
- InputStream getInputStream():
- OutputStream getOutStream():
ps:tcp是面向字节流传输的
tcp回显服务器实现
服务器端
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;public class tcpSever {private ServerSocket socket;public tcpSever(int port) throws IOException {socket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动");//与服务器建立连接 返回一个服务器端SocketSocket clickClient = socket.accept();processConnection(clickClient);}public void processConnection(Socket clientSocket){System.out.println("客户端上线[ip:"+clientSocket.getInetAddress().toString()+" port:"+clientSocket.getPort()+"]");try(InputStream inputStream = clientSocket.getInputStream(); OutputStream outputStream = clientSocket.getOutputStream()) {while(true){//读取结果//输入流 会等待客户端输入东西 阻塞在这里 (通过Scanner的方法都会造成阻塞)Scanner sc = new Scanner(inputStream);if(!sc.hasNext()){//没有数据了 断开连接System.out.println("客户端下线[ip:"+clientSocket.getInetAddress()+" port:"+clientSocket.getPort());socket.close();break;}//遇到换行/空白结束String req = sc.next();//服务器响应String resp = process(req);//OutputStream没有写String的方法//1.将其转换成字节数组//2.使用PrintWriter-----打印流-----字符打印流/字节打印流PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(resp);printWriter.flush();System.out.println("客户端:[req:"+req+" resp:"+resp+" ip:"+clientSocket.getInetAddress()+" port:"+clientSocket.getPort()+"]");}} catch (IOException e) {e.printStackTrace();}}public String process(String req){return req;}public static void main(String[] args) throws IOException {tcpSever sever = new tcpSever(8080);sever.start();} }
客户端
import java.io.IOException; import java.net.*; import java.nio.charset.StandardCharsets; import java.util.Scanner;public class udpClient {//目的端ipprivate String severIp;//目的端端口private int severPort;private DatagramSocket socket;public udpClient(String severIp,int severPort) throws SocketException {socket = new DatagramSocket();this.severIp = severIp;this.severPort = severPort;}public void start() throws IOException {Scanner sc = new Scanner(System.in);System.out.println("客户端启动");while(true){System.out.print(">>");String request = sc.nextLine();if(request.equals("exit")){System.out.println("退出");socket.close();return;}//给出消息发出的目的端ip和端口 (服务器ip端口)DatagramPacket reqPacket = new DatagramPacket(request.getBytes(StandardCharsets.UTF_8),request.getBytes().length,InetAddress.getByName(severIp),severPort);//发送socket.send(reqPacket);//接收响应DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);socket.receive(responsePacket);//转换成字符串形式String res = new String(responsePacket.getData(),0, responsePacket.getLength());//打印响应System.out.println(res);}}public static void main(String[] args) throws IOException {udpClient client = new udpClient("127.0.0.1",8080);client.start();} }
注意:
- 在tcp的Socket中,ip地址可以直接传字符串类型的,不需要像udpSocket那样转成32位整数形式
- 我们读通过next()读,写通过println写。这是设计好了的。使换行符变成我们的消息隔断符,使我们知道每一段消息的头和尾,解决粘包问题
- 在运行tcp回显服务器时,必须先开服务器端,再开客户端,因为如果先开客户端的话,此时没有服务器与之相连,会抛异常------对应的即为tcp的是有连接的