一.什么是网络编程
用户在浏览器中,打开在线视频网站,如优酷看视频,实质是通过网络,获取到网络上的一个视频资源。
与本地打开视频文件类似,只是视频文件这个资源的来源是网络。所谓网络资源就是网络中获取数据。而所有的网络资源,都是通过网络编程来进行数据传输的。网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。
当然,我们只要满足进程不同就行;所以即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程。
发送端和接收端:
在一次网络数据传输时:发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。
接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。
收发端:发送端和接收端两端,也简称为收发端。
注意:发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念。
请求和响应:
一般来说,获取一个网络资源,涉及到两次网络数据传输。第一次:请求数据的发送。第二次:响应数据的发送。
客户端和服务端:
服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务。
客户端:获取服务的一方进程,称为客户端。对于服务来说,一般是提供:
客户端获取服务资源和客户端保存资源在服务端。
常见的客户端服务端模型:
最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:1. 客户端先发送请求到服务端。2. 服务端根据请求数据,执行相应的业务处理。3. 服务端返回响应:发送业务处理结果。4. 客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)
二.UDP和Tcp的特点
这里列举的UDP和Tcp只是很简单的一些特点,后面会详细谈论Tcp和UDP。
udp:无连接,不可靠,全双工,面向数据报。
tcp:有连接,可靠,全双工,面向字节流。
有连接和无连接:JDBC会先创建一个DataSource,再通过DataSource建立Connect连接。比如打电话时对方接通才算建立连接成功。
可靠和不可靠:A尽可能的给B传输信息,传输 失败时,A能感知到。可靠和不可靠都有优缺点,可靠传输效率低,不可靠传输效率高,具体用哪一个要看场景。
字节流和数据报:Tcp和文件操作类似,都是以字节传输的,像流一样。
UDP面向数据报,读写的单位是一个UDP数据报(后面介绍)。
全双工半双工:全双工,一个通道可以双向通信。半双工,一个通道只能单向通信。
三.UDP实现客户端服务器
2个核心类DatagramSocket和DatagramPacket
DatagramSocket
DatagramSocket是一个socket对象,操作系统使用文件这样的概念来管理一些软硬件资源。表示网卡的这类文件被称为socket文件,网卡也是操作系统使用文件来控制的。java的socket对象就对应着socket对象。
第一个构造方法用于客户端,第二个构造方法指定了端口号用于服务器。
为什么服务器要指定一个特定的端口号,而客户端要一个随机的端口号。
比如我要去学校食堂的2号窗口吃饭,学校就是服务器,我就是客户端。为了让学生每次都能找到,我窗口的位置必须是固定的,使用一个固定的端口号。而学生每次吃饭坐的位置不同,上次做的位置可能这次来被别人占用了,所以需要随机分配一个空闲的座位,也就是分配一个随机的端口号。
第一个方法是接收数据报,第二个发送数据报,第三个关闭。前2个方法都传入了DatagramPacket 类型的方法参数,那么DatagramPacke是什么?
DatagramPacke
DatagramPacke是一个UDP数据报,代表了系统中设定的UDP数据报的二进制形式。
DatagramPacket作为UDP数据报,必然要能够承载一些数据,通过手动指定byte[]作为数据的存储空间
实现Udp服务器
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;class UdpSever{public DatagramSocket socket =null;public UdpSever(int port) throws SocketException {this.socket=new DatagramSocket(port);}public void start() throws IOException {System.out.println("客户端启动");while (true){DatagramPacket datagramPacket = new DatagramPacket(new byte[1024],1024);socket.receive(datagramPacket);String str = new String(datagramPacket.getData(),0,datagramPacket.getLength());String requsrt = process(str);DatagramPacket datagramPacket1 = new DatagramPacket(requsrt.getBytes(),requsrt.getBytes().length,datagramPacket.getSocketAddress());socket.send(datagramPacket1);System.out.print("["+datagramPacket.getAddress().toString()+" ");System.out.print(datagramPacket.getPort()+"]:");System.out.print("req:"+str+" ");System.out.println("reqs:"+requsrt);}}public String process(String str){return str;}
}
public class Test3 {public static void main(String[] args) throws IOException {UdpSever udpSever = new UdpSever(9090);udpSever.start();}
}
创建DatagramPacket对象,传入字节数组,DatagramPacket的返回值是一个字节数组,第一句代码运行结束此时并没有把客户端发来的信息写入。socket.reveive才是把客户端发来的信息真正的写入创建的字节数组中。
因为写入字节数组的内容二进制形式,所以构造字符串把接收到的二进制内容转换为字符串。同时计算出响应。
UDP客户端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;class Udpclint{public DatagramSocket socket=null;public String SeverIP;public int SeverPort;public Udpclint(String SeverIP,int SeverPort) throws SocketException {this.SeverIP=SeverIP;this.SeverPort=SeverPort;this.socket=new DatagramSocket();}public void start() throws IOException {System.out.println("客户端启动:");while(true){System.out.print("----->");Scanner scanner = new Scanner(System.in);String str = scanner.next();DatagramPacket datagramPacket = new DatagramPacket(str.getBytes(),str.getBytes().length, InetAddress.getByName(SeverIP),SeverPort);socket.send(datagramPacket);DatagramPacket datagramPacket1 = new DatagramPacket(new byte[1024],1024);socket.receive(datagramPacket1);String requst = new String(datagramPacket1.getData(),0,datagramPacket1.getLength());System.out.println(requst);}}}
public class Test4 {public static void main(String[] args) throws IOException {
Udpclint udpclint = new Udpclint("127.0.0.1",9090);
udpclint.start();}
}
客户端和服务器相互搭配运行效果
根据客户端和服务器代码实现翻译功能
分析:字典功能需要客户端输入中文意思,服务器显示英文单词。只需要客户端在计算响应的时候,实现翻译处理,所以让自己实现的翻译类继承服务器类,重写计算响应的方法(process),使其具备翻译功能
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;class GrammerSever extends UdpSever{Map<String,String> map = new HashMap<>();public GrammerSever(int port) throws SocketException {super(port);map.put("猫","cat");map.put("狗","dog");map.put("鱼","fish");}@Overridepublic String process(String str) {return map.get(str);}
}
public class Test5 {public static void main(String[] args) throws IOException {GrammerSever grammerSever = new GrammerSever(9090);grammerSever.start();}
}
Tcp实现
Tcp分量要比Udp更重,用的更多的协议,,Tcp主要有2个类,SeverSocket和socket。给服务器用seversocket,socket既可以客户端使用也可以服务器使用。
Tcp服务器
mport 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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;class TcpSever{public ServerSocket socket = null;public ExecutorService executorService = Executors.newCachedThreadPool();public TcpSever(int port) throws IOException {this.socket=new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动:");while(true){Socket socket1 = socket.accept();executorService.submit(new Runnable() {@Overridepublic void run() {try {process(socket1);} catch (IOException e) {throw new RuntimeException(e);}}});}}public void process(Socket socket1) throws IOException {System.out.printf("[%s:%d] 客户端上线!\n", socket1.getInetAddress().toString(), socket1.getPort());try(InputStream inputStream = socket1.getInputStream();OutputStream outputStream = socket1.getOutputStream()) {while (true){Scanner scanner =new Scanner(inputStream);if(!scanner.hasNext()){System.out.printf("[%s:%d] 客户端下线!\n", socket1.getInetAddress().toString(), socket1.getPort());break;}String requst = scanner.next();String requse = process1(requst);PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(requse);printWriter.flush();System.out.printf("[%s:%d] req: %s, resp: %s\n", socket1.getInetAddress().toString(), socket1.getPort(),requse, requst);}} catch (IOException e) {throw new RuntimeException(e);}finally {socket1.close();}}public String process1(String str){return str;}
}
public class Test {public static void main(String[] args) throws IOException {
TcpSever tcpSever = new TcpSever(22);
tcpSever.start();}
}
Tcp首先要处理连接,内核的连接就想一个待办事项,这些待办事项在一个队列的数据结构中,应用程序就需要一个一个完成这些任务,要完成这些任务就得先取任务。
accept是把内核中已经建立好的连接拿到应用程序中,但是这里的返回值并非是一个connect这样的对象,而只是一个socket对象,这个socket对象就像一个耳麦一样可以说话也可以听到对方的声音。
Tcp客户端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;class Tcpclint{public Socket socket =null;public Tcpclint(String IP,int port) throws IOException {this.socket=new Socket(IP,port);}public void start(){System.out.println("客户端启动:");try(InputStream inputStream = socket.getInputStream();OutputStream outputStream =socket.getOutputStream()) {while (true) {System.out.print("-------->");Scanner scanner = new Scanner(System.in);String str = scanner.next();PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(str);printWriter.flush();Scanner scanner1 = new Scanner(inputStream);String str1 = scanner1.next();System.out.println(str1);}} catch (IOException e) {throw new RuntimeException(e);}}
}
public class Test2 {public static void main(String[] args) throws IOException {Tcpclint tcpclint = new Tcpclint("127.0.0.1",22);tcpclint.start();}
}
Tcp实现单词翻译功能
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;class Grammer extends TcpSever{Map<String,String> map = new HashMap<>();public Grammer(int port) throws IOException {super(port);map.put("猫","cat");map.put("狗","dog");map.put("鱼","fish");}@Overridepublic String process1(String str) {return map.get(str);}
}
public class Test7 {public static void main(String[] args) throws IOException {Grammer grammer =new Grammer(9090);grammer.start();}
}