一.网络模型与协议:
OSI 七层模式 : 应用层,表示层,会话层,传输层,网络层,链路层,物理层
五层模型: 应用层, 传输层,网络层,链路层,物理层
四层模型 : 应用层, 传输层,网络层,链路层
应用层:http(超文本传输协议) ftp(文件传输协议) stmp (邮件发送协议) pop3(邮件接收协议), ssh ( 安全shell,用于远程登录)
传输层: tcp(安全可靠的协议) udp(不可靠)
网络层:ip
关于ip地址,作用是用来定位到网络上的另一台计算机
查询: windows下可以使用 ipconfig来查看ip地址
linux 下可以使用 ifconfig来查看ip地址
关于端口:作用是用来标记,要访问对方的哪个程序
常用的端口号:
mysql 3306
oracle 1521
sqlserver 1433
redis 6379
tomcat 8080
apache(http的服务) 80
ftp 21
ssh 22
二.传输层协议:
tcp协议:
TCP 协议的特点是: TCP 协议是一个有连接、可靠的协议。所谓有连接,指的是在进行 TCP通信之前,两个需要通信的主机之间要首先建立一条数据通道,就好像打电话进行交流之前,首先要让电话接通一样。所谓可靠,指的是 TCP 协议能够保证: 1、发送端发送的数据不会丢失; 2、接收端接受的数据包的顺序,会按照发送端发送的包的顺序接受。也就是说, TCP协议能够保证数据能够完整无误的传输。
udp协议:
与 TCP 协议相比, UDP 是一个无连接,不可靠的协议。 即:数据的发送方只负责将数据发送出去,数据的接受方只负责接受数据。发送方和接收方不会相互确认数据的传输是否成功。
相对于 TCP 而言, UDP 有一个优点:效率较高。因此,当我们在对数据传输的正确率
不太关心,但是对传输效率要求较高的情况下,可以采用 UDP 协议。典型的使用 UDP 协议的是网络语音以及视频聊天应用
三.java中的网络编程
Socket API 对tcp,udp协议做了封装,能够连接到对方主机,收发数据
tcp的例子:建立连接
服务器端
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest {
public static void main(String[] args) throws IOException {
//传一个端口号与客户端进行连接,端口号一般使用4位以上的数字
ServerSocket ss = new ServerSocket(12345);
System.out.println("等待客户端连接");
//等待客户端连接服务器方法,直到有客户端连接
Socket socket = ss.accept();
//将接收到的数据读入
InputStream is = socket.getInputStream();
byte[] by = new byte[1024];
while(true) {
int len = is.read(by);
if(-1 == len) {
break;
}
System.out.println(new String(by, 0, len));
}
//关闭
socket.close();
}
}
客户端
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;
public class UserTest {
public static void main(String[] args) throws IOException {
//因为连接的是本机,所以将本机地址以及要连接的服务器端口号传入
Socket socket = new Socket("localhost", 12345);
Scanner sc = new Scanner(System.in);
while(true) {
String line = sc.nextLine();
socket.getOutputStream().write(line.getBytes());
}
}
}
上述程序只是进行一个服务器和一个客户端进行一次性对话,接下来实现支持多个客户端连接的服务器
只需要将服务器端进行重写
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
public class ServerTest {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(12345);
//创建一个线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
System.out.println("等待客户端连接");
while(true) {
Socket socket = ss.accept(); //每连接一个新用户调用一次accept()
//让线程池中的线程来处理
threadPool.submit(() -> {
try {
InputStream is = socket.getInputStream(); //读取接收的信息
InetAddress add = socket.getInetAddress(); //获取信息源的地址
byte[] by = new byte[1024];
while(true) {
int len = is.read(by);
if(-1 == len) {
break;
}
System.out.println(add + " " + new String(by, 0, len));
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
}
上面的方式实现了多个客户端向服务端发送消息的功能,但是只有服务端自己能看到消息,所以要进行改进,让聊天室内所有的用户都能看到消息,即将服务器端接收的消息给所有客户端发送一份
![](/qrcode.jpg)
服务器端
import java.io.*;
import java.net.*;
import java.util.concurrent.*;
public class ServerTest {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(12345);
//创建一个线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
//创建一个集合用来存储所有的客户端socket
ConcurrentHashMap<Socket, InetAddress> map = new ConcurrentHashMap<>();
System.out.println("等待客户端连接");
while(true) {
//每接收一个新用户调用一次accept()
Socket socket = ss.accept();
InetAddress add = socket.getInetAddress();
//将新用户添加到集合中
map.put(socket, add);
threadPool.submit(() -> {
try {
InputStream is = socket.getInputStream();
byte[] by = new byte[1024];
while(true) {
int len = is.read(by);
if(-1 == len) {
break;
}
System.out.println(add + " " + new String(by, 0, len));
String content = add + " " + new String(by, 0, len);
//遍历map集合,将内容写到每个用户中
for(Socket userSocket : map.keySet()) {
OutputStream os = userSocket.getOutputStream();
os.write(content.getBytes());
}
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
}
客户端
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Scanner;
public class UserTest {
public static void main(String[] args) throws IOException {
//因为连接的是本机,所以将本机地址以及要连接的服务器端口号传入
Socket socket = new Socket("localhost", 12345);
Scanner sc = new Scanner(System.in);
//创建一个线程,读入发送过来的消息,并进行输出
new Thread(() -> {
try {
InputStream is = socket.getInputStream();
byte[] by = new byte[1024];
while(true) {
int len = is.read(by);
if(-1 == len) {
break;
}
System.out.println(new String(by, 0, len));
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
while(true) {
String line = sc.nextLine();
socket.getOutputStream().write(line.getBytes());
}
}
}