网络编程

基础概念

TCP/IP网络模型

  1. 应用层 负责应用程序的协议,如:HTTP,FTP,DNS

  2. 传输层 是网络程序进行通信,如:TCP,UDP

  3. 网络层 是整个TCP/IP协议的核心,用于将传输的数据进行分组,发送到目标计算机或网络,如:IP,ICMP,IGMP

  4. 链路层 用于定义物理传输通道,如:驱动程序,接口

UDP

无连接通信协议。传输数据时,不建立逻辑连接,直接发送数据。

消耗资源小,效率高,但不保证数据完整性。

适用于丢包后影响不大的应用。

TCP

面向连接的通信协议。先建立逻辑连接,再传输数据。

提供可靠无差错的数据传输。

TCP的三次握手

  1. 客户端向服务器发出连接请求,等待服务器确认。

  2. 服务器向客户端回送一个响应,通知客户端收到连接请求。

  3. 客户端再次向服务器发送确认信息,确认连接。

InetAddress

获取自己的网络信息:

InetAddress.getLocalHost();

获取远程网络信息:

InetAddress.getAllByName("tbphp.net");

UDP示例

发送端:

Scanner sc = new Scanner(System.in);
DatagramSocket client = new DatagramSocket();
InetAddress ip = InetAddress.getByName("127.0.0.1");
while (true) {
System.out.println("请输入要发送的语句:");
byte[] cent = sc.next().getBytes();
client.send(new DatagramPacket(cent, cent.length, ip, 7001));
}

接收端:

DatagramSocket server = new DatagramSocket(7001);
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
while (true) {
server.receive(packet);
System.out.print("[" + packet.getAddress().getHostAddress() + ":" + packet.getPort() + "]\t");
System.out.println(new String(buf, 0, packet.getLength()));
}

TCP聊天案例

Server:

public class Server {
private int port;
private long tid;
private ServerSocket server;
private ArrayList<Socket> clients = new ArrayList<>();
Server(int port) {
this.port = port;
}
public void run() {
start();
accept();
}
/**
* 开启服务
*/
private void start() {
try {
server = new ServerSocket(port);
} catch (IOException e) {
throw new RuntimeException("开启服务失败:" + e);
}
}
/**
* 接收客户端连接
*/
private void accept() {
while (true) {
try {
task(server.accept());
} catch (IOException e) {
System.out.println("获取客户端连接失败:" + e);
}
}
}
/**
* 客户端线程
*
* @param client 客户端
*/
private void task(Socket client) {
tid++;
new Thread(() -> {
clients.add(client);
String clientName = "[访客" + tid + "] ";
int len;
byte[] b = new byte[1024];
boolean isname = true;
try {
InputStream in = client.getInputStream();
while ((len = in.read(b)) != -1) {
if (isname) {
isname = false;
clientName = "[" + new String(b, 0, len) + "] ";
System.out.println("#" + tid + " " + clientName + "连接");
sendAll(clientName + "进入", client);
continue;
}
String msg = clientName + new String(b, 0, len);
System.out.println("#" + tid + " " + msg);
sendAll(msg, client);
}
} catch (IOException ignored) {
}
System.out.println("#" + tid + " " + clientName + "断开");
sendAll(clientName + "离开", client);
clients.remove(client);
}).start();
}
/**
* 广播消息
*
* @param msg 消息内容
* @param client 发送者
*/
private void sendAll(String msg, Socket client) {
byte[] b = msg.getBytes();
int len = b.length;
for (Socket socket : clients) {
if (!socket.isClosed() && socket != client) {
try {
socket.getOutputStream().write(b, 0, len);
} catch (IOException e) {
System.out.println("广播失败:" + e);
}
}
}
}
}

Client:

public class Client {
private String host;
private int port;
private Socket client;
Client(String host, int port) {
this.host = host;
this.port = port;
}
public void run() {
connect();
new Thread(this::read).start();
setName();
write();
}
/**
* 设置姓名
*/
private void setName() {
System.out.println("请输入您的姓名:");
try {
client.getOutputStream().write(new Scanner(System.in).next().getBytes());
} catch (IOException e) {
throw new RuntimeException("姓名设置失败,断开连接");
}
}
/**
* 连接服务器
*/
private void connect() {
try {
client = new Socket(host, port);
} catch (IOException e) {
throw new RuntimeException("连接服务器失败:" + e);
}
}
/**
* 读取信息
*/
private void read() {
try {
InputStream in = client.getInputStream();
int len;
byte[] b = new byte[1024];
while ((len = in.read(b)) != -1) {
System.out.println(new String(b, 0, len));
}
} catch (IOException ignored) {
}
System.out.println("与服务器的连接已断开");
System.exit(0);
}
/**
* 发送信息
*/
private void write() {
try {
OutputStream out = client.getOutputStream();
Scanner sc = new Scanner(System.in);
while (true) {
out.write(sc.next().getBytes());
}
} catch (IOException e) {
System.out.println("发送信息失败:" + e);
}
}
}