赞
踩
客户端: 主动发起请求的一方,客户端给服务器发送的数据“请求”(Request)
服务器: 被动接受请求的一方,服务器给客户端发回的数据“响应”(Response)
服务器不知道什么时候会受到客户端请求,服务器只能一直准备等待7-24小时
源IP: 发件人地址
32位的整数,用三个.分成四个部分,每一个部分一个字节(0-255)
源端口: 发件人姓名
0-65535之间,(占两个字节的整数),一个服务器需要关联一个固定的端口号
目的IP: 收件人地址
目的端口:收件人姓名
协议类型
IP是用来决定互联网上的某个主机的位置. 端口是决定数据交给这个主机.上的哪个程序.
socket对象本质上是一个文件,文件是网卡的抽象
Java标准库中提供了两种风格
1、【UDP】DatagramSocket
面向数据报(发送接收数据,必须以一定的数据包为单位进行传输)
最简单的客户端服务器
客户端给服务器发送一个字符串, 服务器把这个字符串原封不动的返回. (回显服务器echo server)相当于服务器开发中的hello world
如果Packet用于receive,只指定缓冲区就可以,(地址是接收数据的时候由内核填充的)
如果这个Packet用户send,除了指定缓冲区,还需要指定发给谁(用户手动设定). 一种是直接设定InetAddress对象(里面同时包含了IP和port),还可以把IP和port分开设置.
2、【TCP】ServerSocket
面向字节流
服务器必须绑定端口
一个端口号通常情况下,只能被一个进程绑定
服务器绑定了端口之后,客户端才能访问. (买东西的时候需要填好你的收件人地址和收件人姓名,卖家才能发货)
客户端必须不绑定端口(由操作系统自动分配一个空闲端口)
客户端不能绑定的原因:如果客户端绑定了的话,一个主机上就只能启动一个客户端了
最简单的客户端服务器,客户端给服务器发送一个字符串,服务器原封不动返回(回显服务器 echoserver)
1、进行初始化,实例化Socket对象
2、进入主循环(死循环,一直接受请求)
a、读取数据并解析
b、根据请求计算响应
c、把结果写回到客户端
1、进行初始化,实例化Socket对象
private DatagramSocket socket = null;
public UdpEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);
//new这个Socket对象就会让当前的socket对象和一个ip地址以及一个端口号关联(绑定端口)
//未来客户端就按照这个ip+端口访问服务器
//如果在构造socket的时候没写ip默认是0.0.0.0(特殊ip会关联到这个主机的所有网卡)
}
new这个Socket对象就会让当前的socket对象和一个ip地址以及一个端口号关联(绑定端口),未来客户端就按照这个ip+端口访问服务器
如果在构造socket的时候没写ip默认是0.0.0.0(特殊ip会关联到这个主机的所有网卡)
2、进入主循环(死循环,一直接受请求)
a、读取数据并解析
b、根据请求计算响应
c、把结果写回到客户端
DatagramPacket这个类是udp发送接收数据基本单位
IP是用来决定互联网上的某个主机的位置. port是决定数据交给这个主机.上的哪个程序.
public void start() throws IOException { System.out.println("服务器启动"); while (true){ //a. 读取请求并解析 DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);//udp发送接收数据基本单位 //实例化中传入一个缓冲区 //socket对象本质上是一个文件,文件是网卡的抽象 socket.receive(requestPacket);//程序启动马上执行receive,读取从客户端收到的请求 //receive被堵塞,知道真的有数据来,receive就会把数据放入到刚刚new的缓冲区当中 //把收到的请求数据转成一个String(本来是一个byte[]),用trim()是为了去掉数组中的空白部分,提高效率 String request = new String(requestPacket.getData(), 0,requestPacket.getLength()).trim(); //b。根据请求计算响应 String response = process(request); //c。把响应传回给客户端,传回的相应就是response变量,需要包装成Packet对象传回 DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress()); //利用String中的getBytes()方法把response构造成Packet对象,之后是数据的长度, // 还有包要发送给谁,requestPacket.getSocketAddress(),获取客户端地址 //此处的地址就是客户端的地址和端口,这两个信息就包含在requePacket内部 //response.getBytes().length是字节数、response.getBytes().length()是字符数 socket.send(responsePacket); //方便观察打印日志 System.out.printf("[%s:%d] 目的IP :%s; 目的端口号: %s\n",requestPacket.getAddress().toString(), requestPacket.getPort(),request,response ); } } public String process (String s){ //因为是echo server回显服务器,所以process方法只要接收然后直接返回就行,不需要其他操作 //如果更复杂,在这个方法里面可以包含更多的其他操作 return s; }
服务器运行:
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class UdpEchoServer { //1、进行初始化,实例化Socket对象 //2、进入主循环(死循环,一直接受请求) //a、读取数据并解析 //b、根据请求计算响应 //c、把结果写回到客户端 private DatagramSocket socket = null; public UdpEchoServer(int port) throws SocketException { socket = new DatagramSocket(port); //new这个Socket对象就会让当前的socket对象和一个ip地址以及一个端口号关联(绑定端口) //未来客户端就按照这个ip+端口访问服务器 //如果在构造socket的时候没写ip默认是0.0.0.0(特殊ip会关联到这个主机的所有网卡) } public void start() throws IOException { System.out.println("服务器启动"); while (true){ //a. 读取请求并解析 DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);//udp发送接收数据基本单位 //实例化中传入一个缓冲区 //socket对象本质上是一个文件,文件是网卡的抽象 socket.receive(requestPacket);//程序启动马上执行receive,读取从客户端收到的请求 //receive被堵塞,知道真的有数据来,receive就会把数据放入到刚刚new的缓冲区当中 //把收到的请求数据转成一个String(本来是一个byte[]),用trim()是为了去掉数组中的空白部分,提高效率 String request = new String(requestPacket.getData(), 0,requestPacket.getLength()).trim(); //b。根据请求计算响应 String response = process(request); //c。把响应传回给客户端,传回的相应就是response变量,需要包装成Packet对象传回 DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress()); //利用String中的getBytes()方法把response构造成Packet对象,之后是数据的长度, // 还有包要发送给谁,requestPacket.getSocketAddress(),获取客户端地址 //此处的地址就是客户端的地址和端口,这两个信息就包含在requePacket内部 //response.getBytes().length是字节数、response.getBytes().length()是字符数 socket.send(responsePacket); //方便观察打印日志 System.out.printf("[%s:%d] 目的IP :%s; 目的端口号: %s\n",requestPacket.getAddress().toString(), requestPacket.getPort(),request,response ); } } public String process (String s){ //因为是echo server回显服务器,所以process方法只要接收然后直接返回就行,不需要其他操作 //如果更复杂,在这个方法里面可以包含更多的其他操作 return s; } public static void main(String[] args) throws IOException { UdpEchoServer udpEchoServer = new UdpEchoServer(9090);//指定一个端口号 udpEchoServer.start(); } } 服务器启动
服务器一直在等待客户端的请求
站在客户端角度理解此处通信的五元组
协议类型: UDP
源IP: 客户端的IP (客户端所在的主机IP)
源端口: 客户端的端口(操作系统自动分配的端口)
目的IP: 服务器的IP (服务器和客户端在同一个主机上. IP就是127.0.0.1)
目的端口: 9090 (服务器启动的时候绑定的端口)
构造,构造的时候需要传入要连接的服务器ip和端口
客户端的执行分四步
1、从用户读取输入数据
2、构造请求发送给服务器
3、从服务器读取响应
4、把响应写回给客户端
构造,构造的时候需要传入要连接的服务器ip和端口
private DatagramSocket socket = null;
private String serverIp;
private int serverPort;
//构造的时候需要传入要连接的服务器ip和端口
public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
this.serverIp = serverIp;
this.serverPort = serverPort;
socket = new DatagramSocket();//构建客户端socket时不用绑定端口号
//服务器必须绑定端口,客户端必须不绑定端口(由操作系统自动分配一个空闲端口)
//如果客服端绑定,一个主机上就只能启动一个客户端了
}
客户端的执行分四步
public void start() throws IOException { Scanner scanner = new Scanner(System.in); while (true) { //1、读取用户输入的数据 System.out.println("输入数据:"); String request = scanner.nextLine(); if(request.equals("退出")){ break; } //2、构造请求发送给服务器 //里面传入缓冲区,还有缓冲区长度,还有服务器ip 服务器端口号 DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIp),serverPort); socket.send(requestPacket);//发送请求 //3、从服务器读取响应 DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096); socket.receive(requestPacket); //response中放入请求 String response = new String(responsePacket.getData(),0, responsePacket.getLength()); //4、显示响应数据 System.out.println(response); }
客户端总代码
import java.io.IOException; import java.net.*; import java.util.Scanner; public class UdpEchoClient { //构造,构造的时候需要传入要连接的服务器ip和端口 //客户端的执行分四步 //1、从用户读取输入数据 //2、构造请求发送给服务器 //3、从服务器读取响应 //4、把响应写回给客户端 private DatagramSocket socket = null; private String serverIp; private int serverPort; //构造的时候需要传入要连接的服务器ip和端口 public UdpEchoClient(String serverIp, int serverPort) throws SocketException { this.serverIp = serverIp; this.serverPort = serverPort; socket = new DatagramSocket();//构建客户端socket时不用绑定端口号 //服务器必须绑定端口,客户端必须不绑定端口(由操作系统自动分配一个空闲端口) //如果客服端绑定,一个主机上就只能启动一个客户端了 } public void start() throws IOException { Scanner scanner = new Scanner(System.in); while (true) { //1、读取用户输入的数据 System.out.println("输入数据:"); String request = scanner.nextLine(); if(request.equals("退出")){ break; } //2、构造请求发送给服务器 //里面传入缓冲区,还有缓冲区长度,还有服务器ip 服务器端口号 DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIp),serverPort); socket.send(requestPacket);//发送请求 //3、从服务器读取响应 DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096); socket.receive(responsePacket); //response中放入请求 String response = new String(responsePacket.getData(),0, responsePacket.getLength()); //4、显示响应数据 System.out.println(response); } } public static void main(String[] args) throws IOException { UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090); //127.0.0.1表示环回ip,自己访问自己,刚刚所写的服务器和客户端都是在一个主机上,所以使用这个ip //如果不在同一个主机上,此处的ip就要写成要访问的服务器ip,9090就是上面写的服务器的端口号 udpEchoClient.start(); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。