赞
踩
首先介绍了什么是网络编程,随后介绍了接收端发送端、请求响应等基本知识
网络编程:
(1)网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)
(2)在程序员层面上,即要写一个应用程序通过调用传输层提供的API实现网络通信
发送端和接收端:字面意思,发送端是发送数据的,接收端是接收数据的,注意这个概念只是相对的,主要看数据是从哪发到哪。
请求和响应:
一般来说,获取一个网络资源,涉及到两次网络数据传输:
客户端和服务器
服务端:在常见的网络数据传输场景下,给用户提供服务的一方进程
客户端:获取服务的一方进程,是指给用户使用的程序
常见的客户端服务端模型
网络编程是基于Socket开发的,传输层上主要涉及TCP、UDP这两种协议,而他俩给出的API都不同,本段主要介绍概念,包括分类和区别。
(1)Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。
(2)Socket套接字主要针对传输层协议划分为如下三类:
TCP:有连接;可靠传输;面向字节流;有接收缓冲区和发送缓冲区;大小不限;全双工
UDP:无连接;不可靠传输;面向数据报;有接收缓冲区,无发送缓冲区;大小受限,一次最多传输64k;单双工
数据报套接字模型:
流套接字模型:
UDP提供的API主要通过两个类来实现,一个是DatagramSocket,一个是DatagramPacket。
DatagramSocket 方法:代表一个Socket对象,即一个网卡的文件
DatagramPacket 方法:代表一个UDP数据报
因为TCP是字节流式传输,所以不需要一个专门的类来表示TCP数据报
ServerSocket 方法:
(1)给服务器使用的
(2)负责拉客,在外场广撒网。
Socket 方法:
(1)服务端和客户端都能用
(2)像是一个耳麦,可以直接与对方进行通信,负责在内场细致服务。
(1)InetAddress 表示的是IP地址
(2)客户端要设置要传给的服务器的IP地址和端口号是多少,即示例中的 serverIp 和 serverPort 。服务器则是要手动指定一个端口号。
(3)难点在于 DatagramPacket 的构造,涉及了String 和 DatagramPacket 之间的转换。
(4)数据包的接收和发送,依靠 DatagramSocket 的 send 和 receive 方法。
//客户端 public class UdpEchoClient { private DatagramSocket socket = null; private String serverIp; private int serverPort; public UdpEchoClient(String ip, int port) throws SocketException { serverIp = ip; serverPort = port; socket = new DatagramSocket(); } public void start() throws IOException { //获取用户输入的内容 Scanner scanner = new Scanner(System.in); System.out.println("客户端启动!"); //给服务器发送响应 while (true){ System.out.print("-> "); String request = scanner.next(); //不能用request.length(),因为这是指字符长度,我们需要的是字节长度 //除非是纯英文且由utf-8编码,那么两者会相同。Socket是按照字节来处理的。 DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length, InetAddress.getByName(serverIp), serverPort); socket.send(requestPacket); //接收响应 DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096); socket.receive(responsePacket); String response = new String(responsePacket.getData(), 0, responsePacket.getLength()); System.out.println(response); } } public static void main(String[] args) throws IOException { UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090); client.start(); } }
//服务器 public class UdpEchoServer { private DatagramSocket socket = null; public UdpEchoServer(int port) throws SocketException { socket = new DatagramSocket(port); } public void start() throws IOException { System.out.println("服务器启动"); while(true){ DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096); socket.receive(requestPacket); String request = new String(requestPacket.getData(), 0, requestPacket.getLength()); String response = process(request); DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress()); socket.send(responsePacket); System.out.printf("[%s:%d req:%s, resp:%s\n]", requestPacket.getAddress().toString(), requestPacket.getPort(), request, response); } } public String process(String request){ return request; } public static void main(String[] args) throws IOException { UdpEchoServer server= new UdpEchoServer(9090); server.start(); } }
服务器对客户是多对多的关系,一个服务器内核会存许多客户端的连接(建立连接的过程,系统内核会自己完成)。连接就像是一个个代办事项,等待应用程序一个个完成。
serverSocket.accept():将在内核中已经建立好的连接取出来给应用程序
原理是一个【生产者消费者模型】
TCP中的长短连接
缓冲区问题:
创建一个TCP的socket,会同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区
public class TcpServer { private ServerSocket socket = null; private ExecutorService service = Executors.newCachedThreadPool(); public TcpServer(int port) throws IOException { socket = new ServerSocket(9090); } public void start() throws IOException { System.out.println("服务端启动!"); while (true){ Socket clientSocket = socket.accept(); //为了能够接收多个客户端,这里使用了线程池 //当客户端进一步增加,线程数目也会增加,系统的负担也就越来越重,响应速度也就越来越慢 //为了解决这个问题,就需要开源(引入更多的硬件资源)节流(减少每种硬件资源占用的资源) service.submit(new Runnable() { @Override public void run() { try { processConnection(clientSocket); } catch (IOException e) { throw new RuntimeException(e); } } }); } } public void processConnection(Socket clientSocket) throws IOException { try(InputStream inputStream = clientSocket.getInputStream(); OutputStream outputStream = clientSocket.getOutputStream()){ while(true){ Scanner scannerClient = new Scanner(inputStream); String request = scannerClient.next(); String response = process(request); PrintWriter writer = new PrintWriter(outputStream); writer.println(response); writer.flush(); System.out.printf("[%s:%d] req:%s, resp:%s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response); } } catch (IOException e) { throw new RuntimeException(e); }finally { clientSocket.close(); } } public String process(String request){ return request; } public static void main(String[] args) throws IOException { TcpServer server = new TcpServer(9090); server.start(); } }
public class TcpClient { private Socket socket = null; public TcpClient(String serverIp, int serverPort) throws IOException { socket = new Socket(serverIp, serverPort); } public void start(){ System.out.println("客户端启动"); Scanner scanner = new Scanner(System.in); try(InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()){ while (true){ System.out.print("---->"); String request = scanner.next(); PrintWriter writer = new PrintWriter(outputStream); writer.println(request); writer.flush(); Scanner scammerConsole = new Scanner(inputStream); String response = scammerConsole.next(); System.out.println(response); } } catch (IOException e) { throw new RuntimeException(e); } } public static void main(String[] args) throws IOException { TcpClient client = new TcpClient("127.0.0.1", 9090); client.start(); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。