前面我们介绍了通信技术的基础知识,今天我们尝试来用JAVA来创建一个简单的服务器,这个服务器将实现接收客户机发送过来的信息,并把信息再传回给客户机。服务器指的是等待别人来连接的机器。客户机,指的是主动链接别人的机器。
一、JAVA中服务器和客户机的信息传输过程
建议大家在学习JAVA的通信之前先把JAVA中的文件的读取和输出先搞懂。(可以参考我的另一篇博客《JAVA文件读取和写入(内附简单的代码实现)》)。因为服务器和客户机的通信其实就是文件的获取和发送的一种特殊形式。我们以前所了解的文件操作,大多数都是自取自用。就是从自己那里取出要用的文件,处理完成后生成一个新文件件再存到自己那里。InputStream就是读取操作,从自己那里取出要操作的文件。而OutputStream就是写出操作,把处理好的信息写出给自己。而服务器和客户机的通信,从客户机来看,就是客户机发送文件给服务器或者从服务器那里获取文件,从服务器来看,就是服务器发送文件给客户机或者从客户机那里获取文件。
以往所理解的文件操作如下——自娱自乐型
服务器和客户机所进行的通信操作如下——实时互动型
二、涉及到的API类
1.ServerSocket 用来创建一个服务器。操作对象是某一台主机的某个端口端口
2.Socket 创建一个接受服务器信息的中间对象,这个对象就是客户机和服务器进行交互的中介。
它有两个重要的方法,一个是getOutoutStream()——获取客户机发送过来的信息,一个是InputStream()——发送给客户机的信息。也就是说,我们向输入流InputStream写入的数据会被发送到客户端,而我们从输出流OutputStream读取的是客户端发送过来的数据。——这个一定要记住。输入到客户端(input),输出到服务器(output)。
三、源代码
package communicatetest1;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//创建一个测试类
public class communicateTest {
public static void main(String[] args) throws IOException {
//创建一个服务器对象
ServerSocket server=new ServerSocket(9000);
//输出服务器的端口信息
System.out.println("服务器创建成功,端口号为:"+server.getLocalPort());
//创建一个Socket对象来连接,这里不需要新建一个对象,它只要直接引用server即可
Socket socket=server.accept();
//利用socket来接收输出输入流的数据
//这里有一点需要注意,我们向输入流InputStream写入的数据会被发送到客户端,
//而我们从输出流OutputStream读取的是客户端发送过来的数据
OutputStream output=socket.getOutputStream();
InputStream input=socket.getInputStream();
//接着开始进行通信测试
String s="Hello,Welcome to My ServerSocket!";
//这条消息是当客户机连接上我们创建的服务器时,服务器发送给客户机的一条信息
//也就是我们要向客户机发送消息,那么我们应该用的是OutputStream
//首先我们要先将发送信息转化为byte类型,因为输出流的写入方法write()中的参数是byte类型
byte[] dataout=s.getBytes();
//接着调用输出流的写入方法,把信息发送给客户机
output.write(dataout);
//然后让这条信息在命令行中显示出来,以便我们检测信息是否真的已经被发送出去
output.flush();
//关闭连接
socket.close();
}
}
四、运行命令行连接服务器
通过前面的代码我们已经成功地把端口9000设置为开放的服务器,接下来我们就用本地的其他端口作为客户端去连接它,实现把消息传送给客户机。
打开命令行,输入telnet localhost 9000
回车后,出现如下界面,说明连接成功。并且客户机
这时如果我们继续尝试在命令行中连接9000端口,会出现如下报错信息:
这是因为我们代码最后的那条close()指令。在我们成功连接服务器并给客户机发送完消息后,这个端口服务器就被重新关闭了。因此我们连接不上。如果想要重新连接,则需要重新运行一下JAVA代码。
如果我们想让这个服务器每次在关闭后又能重新开启,只需要加上一个while循环。代码如下:
while(true) {
ServerSocket server=new ServerSocket(9000);
System.out.println("服务器创建成功,端口号为:"+server.getLocalPort());
Socket socket=server.accept();
OutputStream output=socket.getOutputStream();
InputStream input=socket.getInputStream();
String s="Hello,Welcome to My ServerSocket!";
byte[] dataout=s.getBytes();
//接着调用输出流的写入方法,把信息发送给客户机
output.write(dataout);
output.flush();
socket.close();
}
这个代码会报错,第二次进入循环时会说这个端口已经被占用了。原因是.close()方法它只是关闭了端口的开放状态,但是这个端口还是会作为一个服务器存在,因为我们的程序还没有结束。只有当我们的程序终止了,这个端口才会重新被初始化。因此当我们第二次又要重新把这个端口设置为服务器时它就会报错。解决方案:移动一下while的位置。改动后如下:
ServerSocket server=new ServerSocket(9000);
System.out.println("服务器创建成功,端口号为:"+server.getLocalPort());
while(true) {
Socket socket=server.accept();
OutputStream output=socket.getOutputStream();
InputStream input=socket.getInputStream();
String s="Hello,Welcome to My ServerSocket!";
byte[] dataout=s.getBytes();
//接着调用输出流的写入方法,把信息发送给客户机
output.write(dataout);
output.flush();
socket.close();
}
这时再去运行就不会有错了。命令运行如下。再次输入连接指令,命令行同样会跳转到这个界面:
五、注意点
1.同一台主机上即可完成通信的操作,因为通信的对象是两个端口,我们可以把同一台主机的某一个端口创建为服务器,然后再用一个端口作为客户机去连接它。
2.同一个端口只能给一个程序通信用,因此这段代码在第二次执行的时候有时会报出端口已被占用的错误,这时只需要更改一下端口号在运行即可。