网络编程基础
什么是网络编程?
在日常生活中,我们可以通过浏览器来查看文档资料、看视频、听音乐,这些都是获取网络资源的方式。而这些网络上的资源是如何显示在我们电脑上的呢?这就是网络编程。即:网络资源通过网络编程来进行数据传输。
官方一点的定义,网络编程是指网络上的主机通过不同的进程,以编程的方式实现网络通信。
当然,即使在一个主机中,只要是不同的进程基于网络来传输数据也属于网络编程。
网络编程的基本概念
发送端:数据的发送方进程,即网络通信中的源主机。
接收端:数据的接收方进程,即网络通信中的目的主机。
收发端:发送端和接收端两端。
请求:请求数据的发送。
响应:响应数据的发送。
客户端:获取服务的一方进程。
服务器:提供服务的一方进程。
上面提到的发送端和接收端其实是相对的:
网络编程实现
Socket套接字
Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议进行网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。
网络通信显然是发生在传输层的过程,而传输层最出名的便是TCP协议和UDP协议了。由于TCP传输和UDP传输的特点不同。因此,Socket也被分为了俩类:TCP的Socket和UDP的Socket。下面我们分别进行介绍:
UDP网络通信流程(回显服务器)
服务器:
- 创建Socket 绑定端口号
//创建一个socket
private DatagramSocket socket = null;
public UdpEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);
}
- 启动服务器
//启动服务器
public void start() throws IOException {
System.out.println("服务器启动");
while (true){
}
}
因为不知道客户端什么时候发送请求,所以使用while(true) 一直保持等待
- 创建Packet来接收客户端传来的数据
//接受客户端传来的数据
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket);
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
转化为字符串更方便使用。 接收数据的字节数组可以根据实际情况选择合适大小
- 根据请求计算响应
//根据请求计算响应
String response = process(request);
//因为是回显服务器 就是返回和请求相同的数据 所以不用处理
public String process(String request){
return request;
}
- 把响应返回给客户端
//把响应返回给客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(responsePacket);
客户端
- 创建Sockket 确定服务器的IP和端口号
//用于网络编程 发送数据
private DatagramSocket socket = null;
//指定发送数据到哪儿
private String serverIP;
private int serverPort;
public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
//先new出socket 不需要指定发送的端口 客户端会自动分配一个空闲端口
socket = new DatagramSocket();
//构造发送去哪儿
this.serverIP = serverIP;
this.serverPort = serverPort;
}
- 启动客户端
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
while (true){
}
}
也需要使用while(true)来时刻准备接收返回的数据
- 发送数据
//1.从控制台读取要发送的数据
System.out.println("请输入要发送的数据");
String request = scanner.next();
//2.构造packet
DatagramPacket requestPacker = new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(this.serverIP),this.serverPort);
//3.发送给服务器
socket.send(requestPacker);
- 接收数据
//1.从服务器读取响应数据
DatagramPacket responsePacker = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacker);
String response = new String(responsePacker.getData(), 0,responsePacker.getLength());
//2.把服务器响应显示在控制台
System.out.println(response);
完整代码示例:
//服务器
public class UdpEchoServer {
//创建一个socket
private DatagramSocket socket = null;
public UdpEchoServer(int port) throws SocketException {
socket = new DatagramSocket(port);
}
//启动服务器
public void start() throws IOException {
System.out.println("服务器启动");
while (true){
//处理一次请求
//1.接受客户端传来的数据
DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
socket.receive(requestPacket);
String request = new String(requestPacket.getData(),0,requestPacket.getLength());
//2.根据请求计算响应
String response = process(request);
//3.把响应返回给客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,
requestPacket.getSocketAddress());
socket.send(responsePacket);
//4.打印一个日志记录当前情况
System.out.printf("%s - %d request: %s response: %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 udpEchoServer = new UdpEchoServer(9090);
udpEchoServer.start();
}
}
//客户端
public class UdpEchoClient {
//用于网络编程 发送数据
private DatagramSocket socket = null;
//指定发送数据到哪儿
private String serverIP;
private int serverPort;
public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
//先new出socket 不需要指定发送的端口 客户端会自动分配一个空闲端口
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.println("请输入要发送的数据");
String request = scanner.next();
//2.构造packet
DatagramPacket requestPacker = new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(this.serverIP),this.serverPort);
//3.发送给服务器
socket.send(requestPacker);
//4.从服务器读取响应数据
DatagramPacket responsePacker = new DatagramPacket(new byte[4096],4096);
socket.receive(responsePacker);
String response = new String(responsePacker.getData(), 0,responsePacker.getLength());
//5.把服务器响应显示在控制台
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1",9090);
udpEchoClient.start();
}
}
TCP网络通信流程(回显服务器)
服务器
- 创建ServerSocket用来监听
//创建一个 监听socket 用于客户端和服务器的连接
private ServerSocket listenSocket = null;
public TcpEchoServer(int port) throws IOException {
listenSocket = new ServerSocket(port);
}
- 启动服务器
//启动服务器
public void start() throws IOException {
System.out.println("服务器启动");
while (true){
}
}
- 建立连接
Socket clientSocket = listenSocket.accept();
- 处理连接
processConnection(clientSocket);
private void processConnection(Socket clientSocket) throws IOException {
System.out.printf("已经和客户端建立连接 ip:%s port: %d \n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
try (InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()){
while (true){
//1.接受请求并解析
Scanner scanner = new Scanner(inputStream);
if (!scanner.hasNext()){
//接受完毕 断开连接
System.out.printf("断开连接 ip: %s port: %d",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("ip: %s port: %d request: %s response: %s\n",clientSocket.getInetAddress().toString(),
clientSocket.getPort(),request,response);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
clientSocket.close();
}
}
- 如果有多个客户端要建立连接,使用线程池加快处理的速度
public void start() throws IOException {
System.out.println("服务器启动");
ExecutorService pool = Executors.newCachedThreadPool();
while (true){
//1.调用listenSocket来建立连接
Socket clientSocket = listenSocket.accept();
//2.处理连接 如果有多个客户端发起请求 这样的响应速度就会很慢
//使用线程池
pool.submit(new Runnable() {
@Override
public void run() {
try {
processConnection(clientSocket);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
客户端
- 尝试建立连接
//创建一个socket来传输数据
private Socket socket = null;
//tcp需要先建立连接再传输数据 就需要先在socket里指定出服务器
public TcpEchoClient(String serverIP,int serverPort) throws IOException {
socket = new Socket(serverIP,serverPort);
}
- 启动服务器
public void start() throws IOException {
}
while(true)放到了传输数据的模块
- 发送请求并接收响应
Scanner scanner = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()){
while (true){
//1.从控制台读取数据 构造请求
System.out.println("请输入要传输的数据");
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);
}
}
完整的代码示例:
//服务器
public class TcpEchoServer {
//创建一个 监听socket 用于客户端和服务器的连接
private ServerSocket listenSocket = null;
public TcpEchoServer(int port) throws IOException {
listenSocket = new ServerSocket(port);
}
//启动服务器
public void start() throws IOException {
System.out.println("服务器启动");
ExecutorService pool = Executors.newCachedThreadPool();
while (true){
//1.调用listenSocket来建立连接
Socket clientSocket = listenSocket.accept();
//2.处理连接 如果有多个客户端发起请求 这样的响应速度就会很慢
//使用线程池
pool.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("已经和客户端建立连接 ip:%s port: %d \n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
try (InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream()){
while (true){
//1.接受请求并解析
Scanner scanner = new Scanner(inputStream);
if (!scanner.hasNext()){
//接受完毕 断开连接
System.out.printf("断开连接 ip: %s port: %d",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("ip: %s port: %d request: %s response: %s\n",clientSocket.getInetAddress().toString(),
clientSocket.getPort(),request,response);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
clientSocket.close();
}
}
//处理响应
public String process(String request){
return request;
}
public static void main(String[] args) throws IOException {
TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
tcpEchoServer.start();
}
}
//客户端
public class TcpEchoClient {
//创建一个socket来传输数据
private Socket socket = null;
//tcp需要先建立连接再传输数据 就需要先在socket里指定出服务器
public TcpEchoClient(String serverIP,int serverPort) throws IOException {
socket = new Socket(serverIP,serverPort);
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream()){
while (true){
//1.从控制台读取数据 构造请求
System.out.println("请输入要传输的数据");
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);
}
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient tcpEchoClient = new TcpEchoClient("127.0.0.1",9090);
tcpEchoClient.start();
}
}
两种实现方式的区别
为什么实现TCP的Socket时要使用多线程而在UDP的Socket不用呢?文章来源:https://www.toymoban.com/news/detail-738600.html
- UDP是无连接的,服务器不关心是哪个客户端发送过来的请求都会进行处理。
- TCP是有连接的,每次建立连接后服务器都得处理对应客户端的多次请求,此时无法接受其他客户端的连接,不满足实际需求。所以得使用多线程来同时处理多个客户端的请求。
为什么TCP的Socket要关闭,ServerSocket和UDP的Socket不用关闭?文章来源地址https://www.toymoban.com/news/detail-738600.html
- Socket也是一个文件,一个进程能同时打开文件的个数是有限的。因为文件描述符表是有限的。
- UDP的Socket不需要建立连接,它在UDP服务器中只有唯一一个对象,不会把文件描述符表占满,随着进程退出会自动释放。
- ServerSocket,它在TCP服务器中只有唯一一个对象,不会把文件描述符表占满,随着进程退出会自动释放。
- Socket,在TCP的服务器中,每次和一个新的客户端建立连接都要创建一个新的。有可能把文件描述符表占满,就需要释放。
到了这里,关于网络编程---Socket的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!