一、套接字概述
1、套接字 ----> IP地址+端口
2、端口号
- 由16位二进制数表示,即十进制数范围为0~65535。
- 0~1023端口为公认端口或熟知端口。
- 如果选择1024以后的端口,注意不要和其他的应用程序端口产生冲突。
3、分类
①客户端套接字
- 发出连接请求的套接字
- 需要指明连接端的IP和端口
- java.net.Socket类
②服务器端套接字
- 接受连接请求的套接字
- 需要指明监听的端口号
- java.net.ServeSocket类
4、 采用TCP进行通信,封装了底层网络的通信细节,使得网络编程变得简单。
二、客户端套接字
1、编程步骤
- ①建立网络连接。需要指出服务器端的IP地址和端口号。
- ②连接成功后,就可以实现数据交互。
数据交互时按照请求—响应模型由客户端向服务器端发送请求,服务器端根据请求内容进行处理,并将响应结果返回给客户端。
注:数据交互的过程可以进行多次,但是每次都要按照请求—响应模型进行。 - ③数据交互结束后。关闭网络连接,释放占用的资源(如端口、内存等),结束客户端程序。
2、构造方法
public Socket(String host,int post) throws UnknownHostException,IOException
public Socket(InetAddress address,int port) throws IOException
3、调用构造方法,创建Socket对象
Socket socket1 = new Socket("www.baidu.com",80);
Socket socket2 = new Socket("127.0.0.1",8000);
socket1实现了连接www.baidu.com这台主机的80端口
socket2实现了连接127.0.0.1这台主机的8000端口
4、数据交互
①交互操作通过输入流/输出流完成
- 发送的请求数据写入到连接对象的输出流中
public OutputStream getOutputStream() throws IOException
- 从连接对象的输入流中读取数据
public InputStream getInputStream() throws IOException
- 获取输入流/输出流的格式
OutputStream out =socket1.getOutputStream(); //建立socket1的输出流对象
InputStream in = socket2.getInputStream(); //建立socket2的输入流对象
- 建立完输入输出流对象后,可以按照I/O流的方式进行数据交互,而且可以通过流的嵌套结构将基本流转换成需要的装饰流,从而方便操作。
②关闭网络连接
socket1.close();
三、服务器端套接字
1、编程步骤
- ①监听固定的端口。(服务器端是处于被动等待连接的状态,不需要发起连接)
- ②当服务器端监听到客户端的连接请求后,就可以与客户端建立一个网络连接。
在这个连接中,包含客户端的相关信息,如IP地址等。 - 连接建立成功后,双方可以进行数据交互了。
- 数据交互结束后,要关闭服务器端,释放占用的资源。
2、采用多线程机制
为解决多用户响应问题,一般采用多线程机制。当获得一个连接后即启动一个专门线程进行处理,主线程继续监听下一个连接请求。
3、构造方法
public ServerSocket(int port) throws IOException
- 该方法创建绑定到特定端口的服务器套接字对象。
- port:连接指定的端口(如果为0,表示使用任何空闲的端口)
连接请求队列:
在建立连接时,会创建一个连接请求队列,表示可以连接的最大请求数(默认为50)。
如果队列满时再收到连接请求,则拒绝该连接。
(下面的构造方法可以解决该问题)
public ServerSocket(int port,int backlog) throws IOException
- 如果backlog的值小于等于0,则使用默认值
4、连接监听
public Socket accept() throws IOException
此方法监听并接收到此套接字的连接,则其在连接传入之前一直阻塞。
四、实例
实现一次数据交互
/**
* 向服务器端发送一个问候字符串:“你好,我是客户机A”,并显示服务器端响应的字符串信息:“你好,我是服务器B”。
* 数据交互只进行一次
*/
import java.net.Socket;
import java.io.*;
public class Example1 {
public static void main(String[] args) {
Socket client_socket = null;
DataInputStream in = null;
DataOutputStream out = null;
String ip = "127.0.0.1";// 服务器IP
int port = 5050;// 服务器端口
try {
client_socket = new Socket(ip, port);// 与服务器创建连接
in = new DataInputStream(client_socket.getInputStream());// 创建输入流
out = new DataOutputStream(client_socket.getOutputStream());// 创建输出流
out.writeUTF("你好,我是客户机A");// 向服务器端发送消息 [writeUTF---->将一个字符串写入基础输出流]
System.out.println("客户机启动,向服务器发送消息:你好,我是客户机A");
String str = in.readUTF();// 等待读取服务器端响应的信息,进入阻塞状态
System.out.println("服务器端的响应信息:" + str);
} catch (Exception e) {
System.out.println(e);
} finally {
try {//关闭网络连接
in.close();
out.close();
client_socket.close();
} catch (Exception e) {
}
}
}
}
/**
* 作为上例的服务器端程序,该程序监听来自客户端的连接请求,显示收到的客户端字符串内容:“你好,我是客户机A”,并回送响应的字符串信息:“你好,我是服务器B”。
* 数据交互只进行一次。
*/
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Example2 {
public static void main(String[] args) {
ServerSocket server_client = null;
Socket socket = null;
DataInputStream in = null;
DataOutputStream out = null;
int port = 5050;
try {
server_client = new ServerSocket(port);//创建绑定端口的服务器端socket
}catch(IOException e) {
System.out.println(e);
}
try {
System.out.println("服务器启动!");
//监听并接收到此套接字的连接。此方法在连接传入之前处于阻塞状态
socket = server_client.accept();
in = new DataInputStream(socket.getInputStream());//创建输入流
out = new DataOutputStream(socket.getOutputStream());//创建输出流
String str = in.readUTF();//从输入流读取字符串,读取结束之前处于阻塞状态
System.out.println("客户机发送过来的信息是:" + str);
out.writeUTF("你好,我是服务器B");//向输入流写入字符串
}catch(IOException e) {
System.out.println(e);
}finally {
try {//关闭网络连接
out.close();
in.close();
server_client.close();
}catch(IOException e) {
System.out.println(e);
}
}
}
}
有两个类,需要先执行Example2类,再执行Example1类。
Example1执行后: