文章目录
- 网络协议分层
- 套接字
- UDP和TCP差异
- UDP的API
- DatagramSocket
- DatagramPacket
- 基于UDP Socket 实现一个回显程序
- TCP的API
- Socket的API
- 基于TCP实现回显程序
网络协议分层
- 应用层: 应用程序拿到数据怎么用
- 传输层: 负责关注传输过程中起点和终点
- 网络层 :负责整个传输过程中的路线规划
- 数据链路层 :负责相邻节点之间的数据传输
- 物理层: 基础设施,硬件设备
套接字
程序员写网络程序,主要编写的应用层代码,真正要发这个数据,调用下层协议,应用层要调用传输层,传输层给应用层提供一组api称为Socket。
系统主要提供socket的API有两种,基于UDP的api和基于TCP的API。
UDP和TCP差异
UDP | TCP |
---|---|
无连接 | 有连接 |
不可靠传输 | 可靠传输 |
面向数据报 | 面向字节流 |
全双工 | 全双工 |
有连接:通信双方各自记录对方的信息
可靠传输:不是100%能够传输成功,而是当我传输失败我能够知道。
面向数据报:以一个UDP数据报为基本单位
面向字节流:以字节为单位进行传输,读写灵活
全双工:一条路径双向通信
UDP的API
DatagramSocket
Datagram:“数据报”
Socket:说明此对象是一个socket对象。
socket对象:相当于对应到系统的一个特殊文件(socket文件),socket文件并非是对应到硬盘的某个区域,而是对应到网卡,所以,要想进行网络通信必须要有socket文件这样的对象,借助这个对象,才能间接操控网卡。往socket对象写数据,相当于通过网卡发送消息,从这个socket对象读数据,相当于通过网卡接受消息。
DatagramPacket
DatagramPacket:这个对象就是一个UDP数据报
基于UDP Socket 实现一个回显程序
- 读取请求并解析
- 根据请求计算响应
- 把响应返回到客户端
package demo;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class demo5 {
//通过网络通信,必须要使用socket对象
private DatagramSocket socket=null;
public demo5(int port) throws SocketException {
//构造的同时绑定端口号,绑定端口不一定成功,可能某个端口被占用
//同一个主机上,一个端口同一时刻,只能被一个进程绑定。
socket=new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
while(true){
DatagramPacket packet=new DatagramPacket(new byte[4096],4096);
socket.receive(packet);//若无数据则会阻塞等待,
//为了防止处理这个请求,我们将把此数据包 转换成String
String str=new String(packet.getData(),0, packet.getLength());//取packet的byte数组
//根据请求计算响应
String s1=process(str);
//根据s1,构造Packet1 ,此处要指定把数据发给谁。
DatagramPacket packet1=new DatagramPacket(s1.getBytes(),s1.getBytes().length,packet.getSocketAddress());
socket.send(packet1);
System.out.println(packet.getAddress().toString()+" "+packet.getPort()+" "+str+" "+s1);
}
}
//回显程序,输入是啥输出是啥。
//后续有其他服务器则会修改此方法,不是回回显了,而是有具体的业务。
private String process(String str) {
return str;
}
public static void main(String[] args) throws IOException {
demo5 demo5=new demo5(9090);
demo5.start();
}
}
package demo;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class demo6 {
//ExecutorService executors=Executors.newCachedThreadPool();
private DatagramSocket socket=null;
private String severIP;
private int severPort;
//客户端启动需要知道服务器在哪,ip地址,端口
public demo6(String severIP,int severPort) throws SocketException {
//对于客户端来说,不需要端口,系统会提供一个空闲端口
this.socket =new DatagramSocket();
this.severIP=severIP;
this.severPort=severPort;
}
public static void main(String[] args) throws IOException {
demo6 demo6=new demo6("127.0.0.1",9090);
demo6.start();
}
public void start() throws IOException {
while(true){
//1.读取控制台字符串
System.out.println("请输入内容");
Scanner sc=new Scanner(System.in);
String request =sc.next();
//2.把字符串构造成UDP packet,并进行发送
DatagramPacket requestPacket= new DatagramPacket(request.getBytes(),0,request.getBytes().length, InetAddress.getByName(severIP),severPort);
socket.send(requestPacket);
//3.客服端尝试读取服务器返回的响应
DatagramPacket responsePacket=new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacket);
//4.把相应数据,转换成String
String response= new String(responsePacket.getData(),0, responsePacket.getLength());
System.out.printf("req: %s,resp:%s\n",request,response);
}
}
}
TCP的API
ServerSocket 是创建TCP服务端Socket的API。
方法签名 | 方法说明 |
---|---|
ServerSocket(int port) | 创建一个服务端流套接字Socket,并绑定到指定端口 |
Socket accept() | 开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待 |
void close() | 关闭套接字 |
Socket的API
方法签名 | 方法说明 |
---|---|
Socket(String host, int port) | 创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接 |
InetAddress getInetAddress() | 返回套接字所连接的地址 |
InputStream getInputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
基于TCP实现回显程序
package demo;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class demo8 {
private ServerSocket socket=null;
ExecutorService exe= Executors.newCachedThreadPool();
public demo8(int port) throws IOException {
this.socket = new ServerSocket(port);
}
public void stack() throws IOException {
System.out.println("启动服务器");
while(true) {
Socket s = socket.accept();
exe.submit(() -> {
try {
processConnection(s);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
public static void main(String[] args) throws IOException {
demo8 d=new demo8(9099);
d.stack();
}
public void processConnection(Socket s) throws IOException {
try(InputStream in=s.getInputStream();
OutputStream out=s.getOutputStream()){
Scanner sc=new Scanner(in);//从字节流转化为字符流
PrintWriter writer=new PrintWriter(out);
while(true){
if(!sc.hasNext()){//读取到数据末尾了
System.out.printf("%s:%d 客服端下线",s.getInetAddress().toString(),s.getPort());
break;
}
//读取数据
String str=sc.next();
//根据请求计算响应
str=process(str);
//写入网卡
writer.println(str);
writer.flush();//刷新缓冲区
}
}
}
private String process(String str) {
return str;
}
}
package demo;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class demo9 {
private Socket socket=null;
public demo9(String ip,int port) throws IOException {
this.socket = new Socket(ip,port);//连接服务器
}
public static void main(String[] args) throws IOException {
demo9 d=new demo9("127.0.0.1",9099);
d.stack();
}
public void stack(){
try(InputStream in=socket.getInputStream();
OutputStream out=socket.getOutputStream()){
Scanner sc=new Scanner(in);//从字节流转化为字符流
PrintWriter writer=new PrintWriter(out);
Scanner scanner=new Scanner(System.in);
while(true){
//读取键盘输入
System.out.println("请输入:");
String request=scanner.next();//获取数据
writer.println(request);//将数据发送到服务器
writer.flush();
//读取响应
String str= sc.next();
//打印到控制台
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}