前面一篇博客我们简单地理清了JAVA创建服务器,以及客户机连接服务器的流程,今天我们继续往下学习,实现客户机和服务器的通信。由于基本概念我们已经在前一篇博客说清楚了,这里我们就不再进行赘述。(不清楚的可以先阅读下我的前一篇博客JAVA通信(一)——输入数据到客户端)
具体代码
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(9005);
//输出服务器的端口信息
System.out.println("服务器创建成功,端口号为:"+server.getLocalPort());
while(true) {
//创建一个Socket对象来连接,这里不需要新建一个对象,它只要直接引用server即可
Socket socket=server.accept();
//利用socket来接收输出输入流的数据
//这里有一点需要注意,OutputStream是向客户机输出信息,而InputStream是读取客户机发送过来的信息
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();
//接收每一个来自客户机的字符
int ascii=input.read();
//如果接收到回车字符就结束循环
while(ascii!=13) {
char accept=(char) ascii;
//输出客户机发出的,服务器收到的每一个字符
System.out.print(accept);
ascii=input.read();
}
//关闭连接
socket.close();
}
}
}
我们在前一篇博客代码的基础上再加上一段代码。利用InputStream的read()方法来实现把信息传送到服务器。需要注意的是read()方法得到的是字符的ascii码,因此我们要用int的变量接收,再把它转化为char型字符。
二、运行命令行如下
输入telnet localhost 9006 后,连接如下
我们尝试输入一些字符,会看到如下界面
每次在命令行中输入一个字符,服务器就会接收到一个字符。而不是我们想象中的写完一句话,按回车后才会发送给服务器。但是这种发送形式不是很适用于我们的日常生活。而且它是不能进行删除的。因为我们每写完一个字符,它就马上被发送到服务器了。当你按下删除键时,它会被识别成一个字符发送给服务器。
三、设置读取发送规则
如果要实现客户机和服务器的正常通信我们就必须为它们设置一定的读取规则,而不能一个个字符地读取。比如我们可以规定回车键为一条完整消息的结束。每次读取到回车键时服务器再一次性地接收消息。当功能逐渐增多时,我们最好把它封装成一个类,而不要总是在主函数里面测试。代码修改如下:
package communicatetest2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//定义一个通信类
public class ServerChat {
OutputStream output;//输出流
InputStream input;//输入流
ServerSocket server;//设置一个服务器对象属性
//定义一个建立服务器的方法
private void setUpServer(int port) throws IOException {
//将输入的端口设置为服务器
server=new ServerSocket(port);
//输出当前服务器的端口号
System.out.println("服务器创建成功,端口号:"+server.getLocalPort());
//定义一个作为中介的接收对象Socket
Socket socket=server.accept();
//为输入输出流赋值
output=socket.getOutputStream();
input=socket.getInputStream();
//开始通信
//传送信息给客户机
String outS="Hello,welcome to my ServerSocket!\r\n";
out(outS);
//发送信息给服务器
ReadString();
}
//定义一个输出信息到客户机的方法
private void out(String outS) throws IOException {
//将字符串转化为byte数组
byte[] dataout=outS.getBytes();
//调用write()将信息发送客户机
output.write(dataout);
//强制输出到命令行的界面中
output.flush();
}
//定义一个传送字符串给服务器的方法
public void ReadString() throws IOException {
String inputS="";
//读取第一个字符
int AsciiNumber=input.read();
while(AsciiNumber!=13) {
//将ascii码转化为相应的char型字符
inputS+=(char)AsciiNumber;
//接收下一个字符
AsciiNumber=input.read();
}
System.out.println(inputS);
output.close();
}
//主函数入口
public static void main(String[] args) throws IOException {
//创建一个通信类的对象
ServerChat server=new ServerChat();
server.setUpServer(9009);
}
}
命令行运行结果如下
如此一下我们就可以实现回车后再进行显示了。但是实质上他还是一个一个字符地传输,只不过当服务器接收到字符时,我们不立即将其显示出来,而是等接受完整句话,我们才把它显示出来。但是它现在还有一个问题,那就是只能传送一句话,这就很难受了。谁说话只说一句呀,因此我们还要对它进行改进。当我们接收完一句话时不要立即关闭客户端和服务器的连接,而是等到用户输入“bye”的时候再断开连接。相关部分的代码更改如下:
public void ReadString() throws IOException {
String inputS="";
while(!inputS.equals("bye")) {
//读取第一个字符
int AsciiNumber=input.read();
while(AsciiNumber!=13) {
//将ascii码转化为相应的char型字符
inputS+=(char)AsciiNumber;
//接收下一个字符
AsciiNumber=input.read();
}
System.out.println(inputS);
}
//关闭连接
output.close();
}
运行命令行结果如下
现在我们就可以进行多条信息的交流啦~
当然现在我们只允许服务器和一个客户机进行交流,当有一个客户机在和服务器在一起交流时,如果第二个客户机尝试和服务器进行连接,就会出现如下报错。
原因很简单,当前我们的只是一个单线程的程序,当有一个客户机在和服务器进行通信时,这个线程就已经被占用了。后期我会继续推出一篇博客来讲解JAVA多线程通信的实现,支持多人聊天。