第1关:Server端监听
任务描述
在本关中,你将被要求在Server端(服务器)监听客户端的连接。
相关知识
Socket编程
两台机器建立一个双向的网络连接实现数据交换,这个双向链路的一端称为一个Socket。它使用TCP协议提供了两台计算机之间的通信机制,客户端程序创建一个套接字,并尝试连接服务器的套接字。
两台计算机之间使用套接字建立TCP连接的一般步骤:
服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。
服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。
在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。
Java针对Socket编程提供的类
ServerSocket类
服务器应用程序通过使用java.net.ServerSocket类以获取一个端口,并且侦听客户端请求。
该类中的主要方法:
Socket类
java.net.Socket类是客户端和服务器用来互相沟通的套接字。客户端通过实例化获取一个Socket对象,而服务器则通过ServerSocket类的accept()方法的返回值获得一个 Socket 对象。
该类中的主要方法:
DatagramSocket类
DatagramSocket类实现了一个发送和接收数据报的socket,传输层协议使用UDP,不能保证数据报的可靠传输。DataGramSocket主要有send, receive和close三个方法。
该类中的主要方法:
SocketAddress
SocketAddress提供了一个socket地址,不关心传输层协议。这是一个虚类,由子类来具体实现功能、绑定传输协议。它提供了一个不可变的对象,被socket用来绑定、连接或者返回数值。
InetSocketAddress
InetSocketAddress实现了IP地址的SocketAddress,也就是有IP地址和端口号表达Socket地址。如果不制定具体的IP地址和端口号,那么IP地址默认为本机地址,端口号随机选择一个。
编程要求
本关的编程任务是补全右侧代码片段中Begin至End中间的代码,具体要求如下:
端口号为6000。
监听客户端的连接。
结束时关闭各个套接字。
评测说明
平台将自动编译补全后的代码,并生成若干组测试数据,接着根据程序的输出判断程序是否正确。
测试输入:
预期输出:
connected!
代码实现
package step1;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public ServerSocket serverSocket;
public Socket socket;
public void start() {
try {
Client client = new Client();
/********** Begin *********/
serverSocket = new ServerSocket(6000);
client.start(); //启动客户端
socket = serverSocket.accept();
serverSocket.close();
socket.close();
/********** End *********/
client.stop(); //关闭客户端
}
catch(Exception e) {
e.printStackTrace();
}
}
}
第2关:Client端消息发送
任务描述
在本关中,你将被要求在Client端(客户端)向服务器发送字符串I am Client!消息。
相关知识
流的含义
流是一个抽象的概念。当Java程序需要从数据源读取数据时,会开启一个到数据源的流。数据源可以是文件,内存或者网络等。同样,当程序需要输出数 据到目的地时也一样会开启一个流,数据目的地也可以是文件、内存或者网络等。
流的目的
流的创建是为了更方便地处理数据的输入输出。
流的分类
流分为字节流和字符流。字节流也称为原始数据,需要用户读入后进行相应的编码转换。而字节流的实现是基于自动转换的,读取数据时会把数据按照JVM的默认编码自动转换成字符。
Java中I/O流与Socket的结合运用
Java中的字节流由InputStream和OutputStream处理,而字符流由Reader和Writer处理。Reader和Writer是Java后加入的处理类,目的是让数据的处理更加方便。
下面是一个客户端向服务器发送字符串Hello World的部分关键代码:
Socket socket=new Socket("127.0.0.1", 1000);
OutputStream outputStream=socket.getOutputStream();
outputStream.write("Hello World".getBytes());
上述Java代码完成了三件事:
根据IP地址与端口与服务器进行连接。
创建OutputStream类的对象outputStream,并得到Socket类的对象socket的输出流。
利用对象outputStream向服务器发送字符串Hello World,利用getBytes()方法将字符串转化为字节数组以便向服务端传递信息。
不要随便关闭对象outputStream,否则会造成对象socket也被关闭。如果只是想关闭对象socket的输出流,使用对象socket的shutdownOutput()方法。
编程要求
本关的编程任务是补全右侧代码片段中Begin至End中间的代码,具体要求如下:
创建OutputStream类的对象,向服务器发送字符串I am Client!。
评测说明
平台将自动编译补全后的代码,并生成若干组测试数据,接着根据程序的输出判断程序是否正确。
本题没有测试样例,平台将比对同学们的程序输出的内容与标准答案。
代码实现
package step2;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
private Socket socket;
private OutputStream socketOutputStream;
public void start() {
try {
socket = new Socket("localhost",6000);
/********** Begin *********/
socketOutputStream = socket.getOutputStream();
socketOutputStream.write("I am Client!".getBytes());
/********** End *********/
socket.shutdownOutput();
}
catch(Exception e) {
e.printStackTrace();
}
}
public void stop() {
try{
socket.close();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
第3关:一次完整通信
任务描述
在本关中,你将被要求在客户端向服务端发送一条消息,同时接收服务端返回的一条消息,从而完成一次完整的通信。
相关知识
Java中InputStream类与Socket的结合运用
下面是一个客户端接收服务器发送的消息的部分关键代码:
Socket socket=new Socket("127.0.0.1", 1000);
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1000];
int length = inputStream.read(bytes);
String string = new String(bytes, 0, length);
System.out.print("Client has recevied: " + string);
inputStream.close();
上述Java代码完成了三件事:
根据IP地址与端口与服务器进行连接。
使用InputStream类创建对象inputStream,并得到Socket类的对象socket的输入流。
利用read()方法读取服务端发来的字节数组消息,并利用String类转化成字符串输出至显示器。
编程要求
本关的编程任务是补全右侧代码片段中Begin至End中间的代码,具体要求如下:
relation()方法中填写接收服务端消息并输出的相关代码。(输出函数已给出,请补全!)
start()方法中填写向服务器发送消息的相关代码,发送的消息为字符串I am Client!。
评测说明
平台将自动编译补全后的代码,并生成若干组测试数据,接着根据程序的输出判断程序是否正确。
本题没有测试样例,平台将比对同学们的程序输出的内容与标准答案。
代码实现
package step3;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
private Socket socket;
private OutputStream socketOutputStream;
private InputStream socketInputStream;
public void relation() {
try{
/********** Begin *********/
socketInputStream = socket.getInputStream();
byte[] bytes = new byte[1000];
int length = socketInputStream.read(bytes);
String serverData = new String(bytes,0,length);
System.out.print("Client has recevied: " + serverData);
/********** End *********/
socket.shutdownInput();
}
catch(IOException e) {
e.printStackTrace();
}
}
public void start() {
try {
socket = new Socket("localhost",6000);
/********** Begin *********/
socketOutputStream = socket.getOutputStream();
socketOutputStream.write("I am Client!".getBytes());
/********** End *********/
socket.shutdownOutput();
}
catch(Exception e) {
e.printStackTrace();
}
}
public void stop() {
try {
socket.close();
}
catch(Exception e) {
e.printStackTrace();
}
}
}