目录
1.网络初始:
2.网络编程:
3.UDP数据报套接字:
4.TCP流套接字:
假设主机A给主机B发了个helloworld 主机A发送的过程一.应用层应用程序会把输入的helloworld构造成约定好的应用层协议的报文应用程序就会把这个应用数据报文,交给传输层协议传输层是操作系统内核实现的,操作系统提供了一些API给应用程序,这些API叫做socket api,代码调用这些api就可以把应用层的数据交给传输层(交给了操作系统内核)二.传输层传输层这里有很多协议,最典型的就是TCP协议,此处以TCP为例TCP协议要在之前的基础上,加上个TCP的协议报头这个TCP报头里面最重要的就是源端口和目的端口!传输层继续将这个数据交给网络层进行处理三.网络层网络层中最典型的就是IP协议IP协议把整个TCP数据看成整体,作为载荷部分,在前头加上IP协议报头IP协议报头里面有很多信息,最关键的就是源IP和目的IP构造好IP数据报之后,IP协议继续把整个数据交给数据链路层四.数据链路层数据链路层的协议有很多,最典型的就是以太网以太网这个歌协议既管数据链路层,又管物理层以太网数据帧将IP数据报的前头加上帧头(源mac地址和目的mac地址),后头加上帧尾(校验和)五.物理层到达物理层的数据已经组织好了就可以通过物理层设备(网卡)把上述数据的二进制bit流转换成光信号或电信号来传输
TCP | UDP |
有连接 | 无连接 |
可靠传输 | 不可靠传输 |
面向字节流 | 面向数据报 |
全双工 | 全双工 |
方法签名 | 说明 |
DatagramSocket() | 一般用于客户端,创建一个UDP数据报套接字的socket,绑定到随机一个端口 |
DatagramSocket(int port) | 一般用于服务器,创建一个UDP数据报套接字的socket,绑定到指定端口 |
void receive(DatagramPacket p ) | 接收数据报,没有收到会阻塞等待 |
void send(DatagramPacket p ) | 发送数据报,不会阻塞等待 |
void close() | 关闭数据报套接字 |
方法签名 | 说明 |
DatagramPacket(byte[ ] b,int length) | 构造一个DatagramPacket用来接收数据报,接收的数据保存在字节数组里 |
DatagramPacket(byte[ ] b,int offset,int length,address) | 构造一个DatagramPacket用来发送数据报,发送的数据为字节数组的指定长度。address为指定目的主机的IP和端口号。 |
getAddress() | 从接收的数据报中获取发送端主机IP地址;或从发送的数据报中获取接收端主机IP地址 |
int getPort() | 从接收的数据报中获取发送端主机端口号;或从发送的数据报中获取接收端主机端口号 |
byte[ ] getData() | 获取数据报中的数据 |
//UDP版本:回显服务器的服务器部分public class UdpEchoServer {private DatagramSocket socket = null;//参数的端口表示服务器要绑定的端口//不需要指定IP,就是本机的IPpublic UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}//启动服务器public void start() throws IOException {System.out.println("服务器启动了!!!");while(true){//1.读取请求并且解析//socket的receive操作需要一个空的requestPacket,receive方法的参数是一个输出型参数//将空的DatagramPacket对象交给receive,在receive里面负责把从网卡读到的数据填充到这个对象中DatagramPacket requestPacket = new DatagramPacket(new byte[1024],1024);//要给DatagramPacket申请内存空间socket.receive(requestPacket);//将DatagramPacket转换成字符串 getData()是获取数据报中的数据,返回一个byte[]String request = new String(requestPacket.getData(),0, requestPacket.getLength());//2.根据请求计算响应String response = process(request);//3.把响应返回给客户端//发送DatagramPacket对象需要指定IP地址和端口号DatagramPacket responsePacket = new DatagramPacket( //getSocketAddress就是得到客户端的IP和端口号response.getBytes(),response.getBytes().length, requestPacket.getSocketAddress());socket.send(responsePacket);//4.打印一个日志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(1025);server.start();}
}
//UDP版本:回显服务器的客户端部分public class UdpEchoClient {private DatagramSocket socket = null;private String serverIP;private int serverPort;//服务器的IP一般不用写,就是本机的IP//需要传服务器的IP和服务器的端口public UdpEchoClient(String serverIp,int serverPort) throws SocketException {socket = new DatagramSocket();//不用指定参数this.serverIP = serverIp;this.serverPort = serverPort;}public void start() throws IOException {Scanner scanner = new Scanner(System.in);while(true){//1.从控制台上读取用户输入的内容System.out.print("-> ");String request = scanner.nextLine();//2.构造一个UDP请求,发送给服务器DatagramPacket requestPacket = new DatagramPacket(//request.getBytes().length这里的length单位是字节//request.length()这里的length()单位是字符,不可以改成这样request.getBytes(),request.getBytes().length, InetAddress.getByName(this.serverIP),this.serverPort);socket.send(requestPacket);//3.从服务器接收响应,并且转成字符串DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);socket.receive(responsePacket);String response = new String(responsePacket.getData(),0, responsePacket.getLength());//4.把响应显示到控制台上System.out.println(response);}}public static void main(String[] args) throws IOException {//IP是某某食堂,端口号是某某窗口UdpEchoClient client = new UdpEchoClient("127.0.0.1",1025);client.start();}
}
方法签名 | 说明 |
ServerSocket(int port) | 创建一个服务端流套接字Socket,并且绑定指定端口 |
Socket accept() | 有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待 |
void close() | 关闭此套接字 |
方法签名 | 说明 |
Socket(String host,int port) | 创建一个客户端流套接字Socket,并且尝试和对应IP的主机上对应端口的进程建立连接 |
InetAddress getInetAddress() | 返回套接字所连接的地址,获取IP和端口 |
int getPort() | 返回套接字所连接的端口 |
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
//TCP版本:回显服务器服务器部分public class TcpEchoServer {private ServerSocket listenSocket = null;public TcpEchoServer(int port) throws IOException {listenSocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动!!");//使用线程池ExecutorService service = Executors.newCachedThreadPool();while(true) {//1.先调用acceptSocket clientSocket = listenSocket.accept();//2.再来处理这个连接,这里应该使用多线程,每个客户端连上来都分配一个新的线程负责处理//使用多线程确实可以解决问题,但是会频繁的创建和销毁线程!
// Thread t = new Thread(()->{
// try {
// processConnection(clientSocket);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// });
// t.start();service.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {e.printStackTrace();}}});}}private void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s:%d] 客户端上线!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());//处理客户端的请求//clientSocket代表的是服务器的网卡,inputStream代表从网卡读数据也就相当于从客户端读取数据try(InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()){while(true){//1.读取请求并且解析Scanner scanner = new Scanner(inputStream);if(!scanner.hasNext()){System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}String request = scanner.next();//2.根据请求计算响应String response = process(request);//3.把响应写回到客户端PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(response);//刷新缓冲区确保数据确实是通过网卡发送出去了printWriter.flush();System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);}} catch (IOException e) {e.printStackTrace();}finally {//为什么这个clientSocket要关闭文件,前面的listenSocket和UDP程序中的socket都不需要关闭文件呢?clientSocket.close();}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9090);server.start();}
}
//TCP版本:回显服务器客户端部分public class TcpEchoClient {private Socket socket = null;public TcpEchoClient(String serverIp, int serverPort) throws IOException {socket = new Socket(serverIp, serverPort);}public void start() {Scanner scanner = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {while (true) {//1.从控制台读取数据System.out.print("-> ");String request = scanner.next();//2.发送请求给服务器PrintWriter printWriter = new PrintWriter(outputStream);printWriter.println(request);printWriter.flush();//3.从服务器上接收响应Scanner respScanner = new Scanner(inputStream);String response = respScanner.next();//4.把响应显示到界面上System.out.println(response);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {TcpEchoClient server = new TcpEchoClient("127.0.0.1",9090);server.start();}}
如果对您有帮助的话,
不要忘记点赞+关注哦,蟹蟹
如果对您有帮助的话,
不要忘记点赞+关注哦,蟹蟹
如果对您有帮助的话,
不要忘记点赞+关注哦,蟹蟹