提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
本文主要记录有关网络编程的一些基础知识以及与socket相关的网络程序。
一、网络编程基础
1.网络编程
网络编程是指网络上的主机,通过不同的进程,以编程的方式实现网络通信(网络数据传输)。通过网络上不同主机,基于网络传输数据资源。
2.基本概念
(1)发送端和接收端 发送端是数据的发送方进程,即网络通信中的源主机。 接收端是数据的接收方进程,即网络通信中的目的主机。 (2)请求和响应 请求数据的发送,响应数据的请求。 (3)客户端和服务端 服务端:是提供服务的一方进程; 客户端:即是获取服务的一方进程。
二、套接字Socket
1.概念
Socket套接字是系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本单元。与Socket套接字的网络程序开发就是网络编程。
2.分类
Socket主要针对传输层协议进行划分: (1)流套接字:使用传输层TCP(传输层控制协议)协议。 (2)数据报套接字:使用传输层UDP(用户数据报协议)协议。 (3)原始套接字:用于自定义传输层协议,用于读写内核没有处理的IP协议数据。
TCP | UDP |
---|
有连接 | 无连接 | 可靠传输 | 不可靠传输 | 面向字节流 | 面向数据报 | 有接收缓冲区,也有发送缓冲区 | 有接收缓冲区,无发送缓冲区 | 大小不限 | 大小受限:一次最多传输64k | 全双工 | 全双工 |
三、UDP数据报套接字编程
1.API
(1)DatagramSocket API 构造方法:DatagramSocket() 、DatagramSocket(int port) 方法:void receive(DatagramPacket p) :从套接字接收数据报; void send(DatagramPacket p):从套接字发送数据报; void close():关闭此数据报套接字。
(2)DatagramPacket API 构造方法:DatagramPacket(byte[] buf,int length):用来接收数据报; DatagramPacket(byte[] buf,int offset,int length,SocketAddress address):用来发送数据报。 方法:InetAddress:从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址; int getPort():获取端口号; byte[] getData():获取数据报中的数据。
(3)InetSocketAddress API 方法:InetSocketAddress(InetAddress addr,int port):创建一个Socket地址。
2.案例
案例1
回响服务,客户端向服务端请求数据,服务端向客户端回应其请求。 服务端 UdpEchoServer(示例):
public class UdpEchoServer {
private DatagramSocket socket = null;
public UdpEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动!");
while (true) {
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket);
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
String response = process(request);
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(responsePacket);
System.out.printf("[%s:%d]; req: %s; res: %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(9090);
server.start();
}
}
客户端 UdpEchoClient(示例):
public class UdpEchoClient {
private DatagramSocket socket = null;
private String serverIP;
private int serverPort;
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) {
System.out.print("->");
String request = scanner.next();
DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(this.serverIP),this.serverPort);
socket.send(requestPacket);
DatagramPacket responsePacket = new DatagramPacket(new byte[4090],4090);
socket.receive(responsePacket);
String response = new String(responsePacket.getData(),0,responsePacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);
client.start();
}
}
案例2
字典客户端和字典服务端。 UdpDIctionaryServer(示例1):
public class UdpDictionaryServer {
private DatagramSocket socket = null;
private HashMap<String,String> dictionary = new HashMap<>();
public UdpDictionaryServer(int port) throws SocketException {
socket = new DatagramSocket(port);
dictionary.put("cat","小猫");
dictionary.put("dog","小狗");
dictionary.put("pig","小猪");
}
public void start() throws IOException {
System.out.println("服务器启动!");
while (true) {
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket);
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
String response = process(request);
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(responsePacket);
System.out.printf("[%s %d] req : %s; res : %s\n",requestPacket.getAddress().toString(),
requestPacket.getPort(),request,response);
}
}
private String process(String request) {
return dictionary.getOrDefault(request,"没有找到该词");
}
public static void main(String[] args) throws IOException {
UdpDictionaryServer server = new UdpDictionaryServer(9595);
server.start();
}
}
UdpDictionaryServer(示例2):
public class UdpDictionaryServer extends UdpEchoServer{
private HashMap<String,String> dic = new HashMap<>();
public UdpDictionaryServer(int port) throws IOException {
super(port);
dic.put("cat","小猫");
dic.put("dog","小狗");
dic.put("pig","小猪");
}
@Override
public String process(String request) {
return dic.getOrDefault(request,"没有找到该词!");
}
public static void main(String[] args) throws IOException {
UdpDictionaryServer server = new UdpDictionaryServer(10010);
server.start();
}
}
UdpDictionaryClient 与UdpEchoClient一样。
四、TCP套接字编程
1.API
(1)SeverSocket API 构造方法:ServerSocket(int port):创建一个服务端流套接字,并绑定到指定端口。 方法:Socket accept():开始监听指定端口,有客户端连接,返回一个Socket对象,并给予该Socket建立与客户端的连接,否则阻塞等待。 void close():关闭此套接字。
(2)Socket API 构造方法:Socket(String host,int port):创建一个客户端流套接字,并与对应IP的主机上,对应端口的进程建立连接。 方法:InetAddress getInetAddress():返回此套接字所连接的地址; InputStream getInputStream():返回此套接字的输入流; OutputStream getOutputStream():返回次套接字的输出流。
2.案例
Tcp版本的回响服务
服务端(包括:使用多线程与线程池) TcpEchoServer(示例):
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 threadPool = Executors.newCachedThreadPool();
while (true) {
Socket clientSocket = listenSocket.accept();
threadPool.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());
try(InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()) {
while (true) {
Scanner scanner = new Scanner(inputStream);
if(!scanner.hasNext()) {
System.out.printf("[%s:%d] 下线\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
break;
}
String request = scanner.next();
String response = process(request);
PrintStream printStream = new PrintStream(outputStream);
printStream.println(response);
printStream.flush();
System.out.printf("[%s:%d] req : %s; res : %s\n",clientSocket.getInetAddress().toString(),clientSocket.getPort(),request,response);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
clientSocket.close();
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer server = new TcpEchoServer(9090);
server.start();
}
}
客户端 TcpEchoClient(示例):
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) {
System.out.print("->");
String request = scanner.next();
PrintStream printStream = new PrintStream(outputStream);
printStream.println(request);
printStream.flush();
Scanner respScanner = new Scanner(inputStream);
String response = respScanner.next();
System.out.println(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient client = new TcpEchoClient("127.0.0.1",9090);
client.start();
}
}
|