网络编程初识
- 网络上不同的主机,以编程的方式实现网络通信(网络数据传输)
- 只要满足进程不同就行
- 即便是同一个主机,只要是不同的进程,基于网络来传输数据,也属于网络编程
网络编程套接字
也叫作 Socket套接字
-
Socket库(Socket API):是操作系统提供给应用程序的一组API(也可以说是程序组件的集合),其用于调用操作系统的网络功能,进行网络通信; 应用层按照指定顺序调用Socket库中的程序,就可以向操作系统内部的协议栈发出委托,委托协议栈向服务器发送消息 -
在协议栈(操作系统中)内部有一块用于存放控制信息的内存空间,这里记录了用于控制通信操作的控制信息,例如通信对象的IP地址、端口号、通信操作的进行状态等 -
套接字(Socket):套接字是一个概念,我们可以说,套接字代表这些控制信息,或者说代表存放控制信息的内存空间 而真正的套接字(套接字的实体),可以通过 netstat -ano查看套接字内容,每一行就相当于一个套接字,当创建套接字的时候,就会在这里添加一行新的控制信息 -
而且协议栈是根据套接字中记录的控制信息来工作的
UDP数据报套接字编程
- 面向数据报
以数据报为单位进行读写 - 无连接
不建立连接,直接通信,相当于“发微信” - 不可靠传输
发送消息后,不知道接收端有没有接收到 - 全双工
可收可发
- DatagramSocket
描述了一个Socket对象,用于发送和接收数据报
构造方法 | |
---|
public DatagramSocket() | 创建一个UDP数据报套接字的Socket,绑定到本机的任意一个端口上(一般用于客户端) | public DatagramSocket(int port) | 创建一个UDP数据报套接字的Socket,绑定到本机的指定端口上(一般用于服务器) |
普通方法 | |
---|
public synchronized void receive(DatagramPacket p) | 接收数据报;没有收到数据报,会阻塞等待 | public void send(DatagramPacket p) | 发送数据报,不会阻塞等待,直接发送 | public void close() | 关闭此套接字 |
构造方法 | |
---|
public DatagramPacker(byte[ ] buf, int offset, int length) | 用来接收数据,接收的数据保存在字节数组中,从数组的指定位置开始放,注意length是接收指定长度(字节)的数据,此处需要双方约定好 | public DatagramPacket(byte buf[ ], int offset, int length,InetAddress address, int port) | 用来发送数据,发送的数据为字节数组[offset, offset+length),address为目的IP,port为目的端口号 | public DatagramPacket(byte buf[ ], int offset, int length,SocketAddress addres) | 用来发送数据,address中包含目的IP和目的端口号 |
普通方法 | |
---|
public synchronized InetAddress getAddress() | 从接收的数据报中,获取发送端主机的IP地址;或从要发送的数据报中,获取接收端主机的IP地址 | public synchronized int getPort() | 从接收的数据报中,获取发送端主机的端口号;或从要发送的数据报中,获取接收端主机的端口号 | public synchronized byte[ ] getData() | 获取数据报的内容 | public synchronized int getLength() | 获取数据报的长度 | public synchronized SocketAddress getSocketAddress() | 从接收的数据报中,获取发送端主机的SocketAddress;或从要发送的数据报中,获取接收端主机的SocketAddress |
InetAddress 表示IP地址 比较重要的两个方法
- public static InetAddress getByName(String host)
将 字符串 转变为 InerAddress(ip地址) - public String toString()
将 InerAddress(ip地址)转变为 字符串
socketAddress 是一个抽象类
- InetSocketAddress 继承自 socketAddress,其中又维护了一个 InetSocketAddressHolder静态内部类,有字段保存Socket的IP地址、端口号、域名
- int getPort()
- InetAddress getAddress()
- String getHostName()
实现一个Udp协议的客户端服务器程序
服务器程序
package net;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class UdpServer {
private DatagramSocket socket = null;
public UdpServer(int port) throws SocketException {
socket = new DatagramSocket(port);
}
public void start() throws IOException {
while(true) {
byte[] bytes = new byte[1024];
DatagramPacket requestPacket = new DatagramPacket(bytes, bytes.length);
socket.receive(requestPacket);
String request = new String(bytes, 0, requestPacket.getLength());
String response = process(request);
InetAddress clientAddress = requestPacket.getAddress();
int port = requestPacket.getPort();
byte[] b = response.getBytes();
DatagramPacket responsePacket = new DatagramPacket(b, 0, b.length, clientAddress, port);
socket.send(responsePacket);
System.out.printf("[%s : %d] req: %s; resp: %s \n", clientAddress.toString(), port, request, response);
}
}
public String process(String request){
return "拼搏百天,我要去" + request + "!!!";
}
public static void main(String[] args) throws IOException {
UdpServer server = new UdpServer(9960);
server.start();
}
}
客户端程序
package net;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class UdpClient {
private DatagramSocket socket ;
private InetAddress ipServer ;
private int portServer;
public UdpClient(String ipServer, int portServer) throws SocketException, UnknownHostException {
this.ipServer = InetAddress.getByName(ipServer);
this.portServer = portServer;
socket = new DatagramSocket();
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println(">");
String request = scanner.next();
if(request.equals("exit")){
System.out.println("bye-bye!!!");
break;
}
byte[] bytes = request.getBytes();
DatagramPacket requestPacket = new DatagramPacket(bytes, 0, bytes.length, this.ipServer, this.portServer);
socket.send(requestPacket);
byte[] b = new byte[1024];
DatagramPacket responsePacket = new DatagramPacket(b, 0, b.length);
socket.receive(responsePacket);
String response = new String(b, 0, responsePacket.getLength());
System.out.printf("req: %s; resp: %s \n", request, response);
}
}
public static void main(String[] args) throws IOException {
UdpClient client = new UdpClient("localhost", 9960);
client.start();
}
}
TCP套接字编程
- 面向字节流
以字节为单位进行读写 - 有连接
通信前先打招呼,相当于“打电话” - 可靠传输
发送消息后,可以知道接收端有没有接收到消息 - 全双工
可收可发
构造方法 ServerSocket(int port) 创建一个服务端Socket,绑定到指定端口
普通方法 Socket accept() 监听指定端口(创建时绑定的端口),有客户端连接后,返回 与客户端建立连接的 服务端Socket,没有客户端连接时就阻塞等待
服务器的应用程序先使用ServerSocket操纵网卡,有客户端连接时,再使用Socket操纵网卡
- Socket
客户端 Socket 或 服务器中,与客户端建立连接的 服务端Socket
构造方法 Socket(String host, int port) Socket(InetAddress ip, int port) 创建客户端Socket,参数是 服务器的Ip和端口号(目的Ip、目的端口)
普通方法 | |
---|
public InetAddress getInetAddress() | 得到Socke连接的地址 | public int getPort() | 得到Socke连接的端口号 | public InputStream getInputStream() | Socket的输入流,从Socket中取数据 | public InputStream getInputStream() | Socket的输出流,往Socket中放数据 |
关于客户端Socket
- 实例化客户端Socket的时候,客户端与服务器就建立连接
- 客户端的端口是随机分配一个未被占用的端口
- 每个客户端与服务器之间都有一个独立的Socket连接,当一个客户端与服务器通信结束时,注意关闭 它的服务端Socket,释放资源
- 使用多线程,实现多个客户端同时与服务器通信
关于 服务器中ServerSocket和Socket的关系
- ServerSocket 监听指定的端口,获取到与 客户端建立连接的 Socket(也可以把Socket看作是连接)
- Socket 就是 与客户端建立连接的 服务端Socket
- 比如,马路牙子上的房产中介,我想买房,他就拉着我到售楼处,会有专业的 销售 来向我介绍楼盘,然后中介继续去马路上“蹲守”;
我就类似于客户端Socket,房产中介就类似于ServerSocket,销售 就类似于 服务端Socket,销售向我介绍楼盘 就类似于与网络通信
实现一个 TCP协议的客户端服务器程序
服务器程序
package net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class TcpServer {
private ServerSocket serverSocket = null;
public TcpServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
ExecutorService pool = Executors.newCachedThreadPool();
while(true){
Socket socket = serverSocket.accept();
String clientIp = socket.getInetAddress().toString();
int clientPort = socket.getPort();
System.out.printf("与 [%s : %d] 建立连接!!!", clientIp, clientPort);
Thread t = new Thread( () -> {
communicate(socket);
});
t.start();
}
}
public void communicate(Socket socket) {
try (InputStream in = socket.getInputStream()) {
try (OutputStream out = socket.getOutputStream()) {
Scanner scanner = new Scanner(in);
PrintWriter writer = new PrintWriter(out);
String clientIp = socket.getInetAddress().toString();
int clientPort = socket.getPort();
while (true) {
String request = null;
if (scanner.hasNext()) {
request = scanner.nextLine();
} else {
System.out.printf("[%s : %d] 断开连接!!!", clientIp, clientPort);
break;
}
String response = process(request);
writer.println(response);
writer.flush();
InetAddress ip = socket.getInetAddress();
int port = socket.getPort();
System.out.printf("[%s : %d] req: %s; resp: %s \n", ip.toString(), port, request, response);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public String process(String request){
return "拼搏百天, 我要去" + request + "!!!";
}
public static void main(String[] args) throws IOException {
TcpServer server = new TcpServer(8088);
server.start();
}
}
客户端程序
package net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;
public class TcpClient {
private Socket socket;
private InetAddress serverIp;
private int port;
public TcpClient(String serverIp, int port) throws IOException {
this.serverIp = InetAddress.getByName(serverIp);
this.port = port;
socket = new Socket(serverIp, port);
}
public void start(){
try (InputStream in = socket.getInputStream()){
try (OutputStream out = socket.getOutputStream()){
Scanner scanner = new Scanner(System.in);
Scanner sc = new Scanner(in);
PrintWriter writer = new PrintWriter(out);
socket = new Socket(serverIp, port);
while(true){
System.out.print("> ");
String request = scanner.nextLine();
if(request.equals("exit")){
break;
}
writer.println(request);
writer.flush();
String response = sc.nextLine();
System.out.printf("req: %s; resp: %s", request, response);
System.out.println();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpClient client = new TcpClient("localhost", 8088);
client.start();
}
}
|