目录
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,就是本机的IP
public 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.先调用accept
Socket clientSocket = listenSocket.accept();
//2.再来处理这个连接,这里应该使用多线程,每个客户端连上来都分配一个新的线程负责处理
//使用多线程确实可以解决问题,但是会频繁的创建和销毁线程!
// Thread t = new Thread(()->{
// try {
// processConnection(clientSocket);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// });
// t.start();
service.submit(new Runnable() {
@Override
public 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();
}
}
如果对您有帮助的话,
不要忘记点赞+关注哦,蟹蟹
如果对您有帮助的话,
不要忘记点赞+关注哦,蟹蟹
如果对您有帮助的话,
不要忘记点赞+关注哦,蟹蟹