Java利用Socket实现网络通信功能
作者:一一哥Sun
一. Socket编程
1. 简介
Socket编程是基于TCP/IP协议的网络编程技术,它给我们提供了一种可以用于网络通信的机制,让我们能在不同的计算机之间进行数据交换。我们可以利用Socket编程实现客户端/服务器程序的开发,如聊天室、FTP客户端等项目。
2. 通信流程
对于Socket编程,我们要重点搞清楚客户端与服务器端之间的通信流程。
在Socket编程中有两种类型的Socket:服务器Socket和客户端Socket。服务器Socket可以在服务器上创建用于监听客户端请求的端口,客户端Socket则可以在客户端上创建用于连接服务器的Socket。客户端Socket向服务器Socket发送请求,服务器Socket可以接收该请求,并创建一个新的Socket用于与客户端通信。通过这种方式,客户端和服务器端之间就可以进行数据交换。
在Socket编程中,客户端和服务器之间会通过TCP/IP协议进行通信。客户端Socket首先回连接到服务器Socket的IP地址和端口号,这样就会建立起一个TCP连接。一旦连接建立,客户端和服务器之间就可以进行数据传输。数据会被分割成数据包,并通过TCP/IP协议在客户端和服务器之间传输。
然后服务器Socket会在服务器端创建一个用于监听客户端请求的端口,客户端Socket则在客户端上创建用于连接服务器的Socket。客户端Socket向服务器Socket发送连接请求,服务器Socket接收该请求并创建一个新的Socket用于与客户端通信。通过这种方式,客户端和服务器端之间就可以进行数据交换。
这样,客户端和服务器端之间就通过TCP/IP协议实现了Socket通信。
3. 核心API
我们可以使用java.net包中的API来实现Socket编程,这些常用的API包括:
- ServerSocket类:用于创建服务器端Socket,监听客户端发来的请求。
- Socket类:用于创建客户端Socket,可以连接服务器Socket。
- InputStream和OutputStream类:用于在Socket之间传输数据的输入输出流。
4. 基本案例-单向通信
接下来先给大家编写一个可以实现单向通信的基本案例,包括一个服务器端和一个客户端。这个案例中,客户端给服务器端发送一条消息,然后服务器把客户端发来的消息打印出来,代码如下:
4.1 服务器端
下面是服务器端的代码案例。在本案例中,主要是利用ServerSocket对象定义了一个服务器,并通过accept()方法来获取与该服务器绑定的Socket客户端对象。这里要注意端口号“1234”是我们自己随意定义的,只要不与其他程序的端口号重合即可。然后我们可以在Socket客户端对象上通过getInputStream()方法来获取一个InputStream输入流,进而读取客户端发来的消息。最后大家要记得把各种IO流资源释放。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; /** * @author 一一哥Sun * @company 千锋教育 */ public class MySocketServer { public static void main(String[] args) throws IOException { //设置服务器的端口号 int portNumber = 1234; // 创建服务器套接字并绑定端口号 ServerSocket serverSocket = new ServerSocket(portNumber); //获取与ServerSocket关联的Socket客户端对象 Socket acceptSocket = serverSocket.accept(); // 服务器接收客户端发来的消息 InputStream serverInputStream = acceptSocket.getInputStream(); BufferedReader in = new BufferedReader(new InputStreamReader(serverInputStream)); String response = in.readLine(); if (response != null) { System.out.println("服务器接收客户端发来的消息===>: " + response); } // 关闭套接字和流 serverSocket.close(); acceptSocket.close(); serverInputStream.close(); } }
大家要注意,在今天的案例中,没有使用循环来一直进行消息的收发,如果我们想实现不间断的消息收发,可以把相关代码写在循环体中。所以在今天的案例中,消息收发一次之后,项目就会停掉。
4.2 客户端
接着又定义了一个客户端程序。在该程序中,定义了一个Socket客户端对象,该对象通过IP地址和端口号来关联服务端对象。因为小编的项目,客户端和服务端是在同一台机器上,所以这里的IP地址我们可以使用localhost,当然也可以用服务端的实际IP地址。然后利用字符流和字节输出流给服务端发送信息,最后释放IO流资源。
import java.io.IOException; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; /** * @author 一一哥Sun * @company 千锋教育 */ public class MySocketClient { public static void main(String[] args) throws IOException { try { // 创建Socket对象并连接到服务器,关联服务器的ip地址与端口号 Socket socket = new Socket("localhost", 1234); // 创建输入输出流,通过套接字发送和接收数据 Scanner scanner = new Scanner(System.in); String nextLine = scanner.nextLine(); // 输出流,客户端向服务器发送消息 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); out.println(nextLine); // 关闭套接字和流 out.close(); scanner.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
在这个案例中,主要是利用ServerSocket、Socket和IO流这三种API,就实现了客户端给服务端发送消息的功能。但是这个案例中,我们只能是客户端给服务端发送消息,服务端却不能给客户端回复消息,所以接下来要把这个案例改进一下,实现客户端与服务端之间互相传递消息。
5. 改进案例-双向通信
接下来我们就把上面的案例改进一下,实现客户端与服务器端之间的双向通信,即客户端给服务器端发送消息,服务器端收到消息之后,再给客户端返回消息。
5.1 服务器端
在下面的代码中,小编把服务器端的代码改进了一下,在if语句中增加了利用OutputStream向客户端返回信息的代码,如下所示:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; /** * @author 一一哥Sun * @company 千锋教育 */ public class MyServer { public static void main(String[] args) throws IOException { int portNumber = 1234; // 创建服务器套接字并绑定端口号 ServerSocket serverSocket = new ServerSocket(portNumber); // 注意:服务器只能接受一次客户端!用该socket对象既可以接收客户端发来的消息,也可以给客户端回复消息 Socket acceptSocket = serverSocket.accept(); // 服务器接收客户端发来的消息 InputStream serverInputStream = acceptSocket.getInputStream(); BufferedReader in = new BufferedReader(new InputStreamReader(serverInputStream)); String response = in.readLine(); if (response != null) { System.out.println("服务器接收客户端发来的消息===>: " + response); // 服务器向客户端发送响应信息 OutputStream serverOutputStream = acceptSocket.getOutputStream(); String serverResponse = "我是服务器,你的消息已收到!"; serverOutputStream.write(serverResponse.getBytes()); serverOutputStream.flush(); serverOutputStream.close(); } // 关闭套接字和流 serverSocket.close(); acceptSocket.close(); serverInputStream.close(); //serverOutputStream.close(); } }
5.2 客户端
在下面的代码中,把客户端的代码也改进了一下,主要是增加了利用BufferedReader和InputStream,读取服务器返回信息的代码,如下所示:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; /** * @author 一一哥Sun * @company 千锋教育 */ public class MyClient { public static void main(String[] args) throws IOException { try { //创建Socket对象并连接到服务器 Socket socket = new Socket("localhost", 1234); //创建输入输出流,通过套接字发送和接收数据 Scanner scanner=new Scanner(System.in); String nextLine = scanner.nextLine(); //输出流,客户端向服务器发送消息 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); out.println(nextLine); //输入流,客户端从服务器接收消息 BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); String response = in.readLine(); System.out.println("Server服务器返回的响应信息===>: " + response); //关闭套接字和流 out.close(); in.close(); scanner.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
经过以上代码的改造,我们就实现了服务器与客户端之间的双向通信,大家可以把小编的代码案例运行起来看看效果。
6. 注意事项
大家要注意,在进行Socket通信开发时,稍有不慎可能就会出现各种问题,所以我们要注意以下事项:
- 在每个Socket连接中,我们都应该严格按照协议规定的格式进行数据的发送和接收,否则就可能会导致数据传输失败或被误解。
- 在Socket编程中,应该使用多线程或异步机制等技术来避免阻塞。如果阻塞时间过长,可能会导致客户端或服务器崩溃。在上面的案例中,是把收发消息的代码之间写在了主线程中,其实我们可以把这种耗时的操作放在Thread或线程池中进行实现。
- 服务器端应该可以同时处理多个客户端的请求,否则有可能会导致客户端的请求被拒绝或服务器崩溃。
- 我们要保证Socket编程的保安全性,例如防止黑客攻击、拒绝服务攻击等恶意行为。
- 我们还应该有适当的错误处理,例如必须处理网络连接失败、数据传输的失败等异常情况。
除了以上几点注意事项,还有其他一些细节需要注意。在Socket编程中,必须对网络通信有深入的理解和掌握,才能确保程序的正确性和安全性。
二. 结语
今天的文章,主要是给大家介绍了Socket编程的实现过程。其实Socket通信主要就是分为服务器端和客户端,有着比较清晰的实现逻辑,学习起来并不难,大家重点理解和掌握Socket通信的实现流畅即可。