Socket:原义为“孔”或“插座”。通常也称套接字,用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。应用程序通常通过“套接字”向网络发出请求或者应答网络请求。
以java为例,要进行网络编程的开发,最为核心的两个类是ServerSocket和Socket。ServerSocket用于服务器端,用于接收用户的请求,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。
Socket之间连接过程(来自百度百科):
(1)服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
(2)客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
(3)连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
一个小例子:
创建服务端:
public class HelloServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(9999); // 所有的服务器必须有端口
System.out.println("等待客户端连接...");
/**
* (1)服务器监听:
* 是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
*
* (3)连接确认:
* 是指当服务器端套接字监听到或者说收到客户端套接字的连接请求,它就响应客户端套接字
* 的请求,建立一个新的线程,把服务端套接字的描述发送给客户端,一旦客户确认了此描述,
* 连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的请求。
*/
Socket socket = server.accept();//等待客户端连接. accept()方法有阻塞的效果
// OutputStream并不方便输出,所以利用打印流完成输出
// 当客户端连接上的时候,往客户端传递数据。socket.getOutputStream(),是指向客户端输出数据
PrintStream out = new PrintStream(socket.getOutputStream());
out.println("Hello World!");
out.close();
socket.close();
server.close();
}
}
创建客户端:
public class HelloClient {
public static void main(String[] args) throws Exception {
/**
* (2)客户端请求:
* 是指由客户端的套接字提出连接请求,要连接的目标是服务端的套接字。为此,
* 客户端套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,
* 然后就向服务器端套接字提出连接请求。
*/
Socket socket = new Socket("localhost",9999); // 连接服务器端
// 取得客户端的输入数据流对象,表示接收服务器端的信息
Scanner scan = new Scanner(socket.getInputStream());
scan.useDelimiter("\n");
if (scan.hasNext()) {
System.out.println("[回应数据]" + scan.next());
}
scan.close();
socket.close();
}
}
先运行服务端,再运行客户端,操作很简单,就不展示结果了。
小结:
不管是客户端还是服务器端,建立连接之后,两端都会产生一个Socket对象,如果两段要进行通信,那么通信的过程可以理解为:
- 客户端:如果要接收服务端的信息,使用socket.getInputStream()获得输入数据流对象;
如果要向服务端发送信息,使用socket.getOutputStream()输出数据流对象。 - 服务端:如果要接收客户端的信息,使用socket.getInputStream()获得输入数据流对象,
如果向客户端发送信息,使用socket.getOutputStream()输出数据流对象
至于最终怎么发送,怎么获取,有很多中方式,随意选。
Echo程序
在网络编程之中Echo是一个经典的程序开发模型,本程序的意义在于:客户端随意输入信息,并且将信息发送到服务器端,服务器端接收后前面加上一个“ECHO:”的标记返回。
本程序设计如下:
由于需要采用多次输入的形式,所以不能够每次连接后立刻关闭服务器端;
可以设置一个字符串,如果输入了“byebye”,那才表示结束本次的Echo操作。
创建服务端:
public class EchoServer {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(9999);
Socket client = server.accept(); // 连接客户端
// 得到客户端输入数据以及向客户端输出数据的对象
Scanner scan = new Scanner(client.getInputStream());
PrintStream out = new PrintStream(client.getOutputStream());
boolean flag = true;
while(flag) {
if (scan.hasNext()) {
String str = scan.next().trim(); // 得到客户端发送的内容
if (str.equalsIgnoreCase("byebye")) {
out.println("拜拜,再会!");
flag = false;
} else {
out.println("ECHO:" + str);
}
}
}
scan.close();
out.close();
client.close();
server.close();
}
}
创建客户端:
public class EchoClient {
public static void main(String[] args) throws Exception {
Socket client = new Socket("localhost", 9999);
Scanner input = new Scanner(System.in);
Scanner scan = new Scanner(client.getInputStream());
input.useDelimiter("\n");
scan.useDelimiter("\n");
PrintStream out = new PrintStream(client.getOutputStream());
boolean flag = true;
while (flag) {
System.out.println("请输入要发送数据:");
if (input.hasNext()) {
String str = input.next().trim();
out.println(str); // 客户端向服务器端发送数据
if (str.equalsIgnoreCase("byebye")) {
flag = false;
}
if (scan.hasNext()) {
System.out.println(scan.next()); // 输出回应数据
}
}
}
input.close();
scan.close();
out.close();
client.close();
}
}
先运行服务端,再运行客户端,客户端出现如下输出:
请输入要发送数据:
你好
ECHO:你好
请输入要发送数据:
我好
ECHO:我好
请输入要发送数据:
byebye
拜拜,再会!
上面的Echo还只能连接一个客户端,也就是说是单线程的,实际中一般会是多线程请求,所以改善服务端代码如下,每连接一个客户端就创建一个线程:
class EchoThread implements Runnable{
private Socket client;
public EchoThread(Socket client) {
this.client = client;
}
@Override
public void run() {
Scanner scan;
try {
scan = new Scanner(client.getInputStream());
PrintStream out = new PrintStream(client.getOutputStream());
boolean flag = true;
while(flag) {
if (scan.hasNext()) {
String str = scan.next().trim(); // 得到客户端发送的内容
if (str.equalsIgnoreCase("byebye")) {
out.println("拜拜,再会!");
flag = false;
} else {
out.println("ECHO:" + str);
}
}
}
scan.close();
out.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 处理多线程,可以多个客户端连接
*/
public class EchoServer2 {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(9999);
boolean flag = true;
while (flag) {
Socket client = server.accept();
new Thread(new EchoThread(client)).start();
}
server.close();
}
}
源码点这里。
笔记就记到这里了。