Java中的TCP/UDP网络通信编程(二)
第1个构造方法来创建客户端的Socket对象,并与服务器建立连接,是非常简单和方便的.
服务器端程序调用ServerSocket.accept方法等待客户端的连接请求,一旦accept接收了客户端连接请求,该方法返回一个与该客户端建立了专线连接的Socket对象,不用程序去创建这个Socket对象.建立了连接的两个Socket是以IO流的方式进行数据交换的,Java提供了Socket.getInputStream返回Socket的输入流对象,Socket.getOutputStream返回Socket的输出流对象.
2.3,TCP程序例子的服务器程序:
[java]
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(8000);
Socket s = ss.accept();
InputStream ips = s.getInputStream();
OutputStream ops = s.getOutputStream();
ops.write("hello,World!".getBytes());
byte[] buf = new byte[1024];
int len = ips.read(buf);
System.out.println(new String(buf,0,len));
ips.close();
ops.close();
s.close();
ss.close();
}
}
在这个程序里,创建了一个在8000端口上等待连接的ServerSocket对象,当接收到一个客户的连接请求后,程序从与这个客户建立了连接的Socket对象中获得输入输出流对象,通过输出流首先向客户端发送一串字符,然后通过输入流读取客户端发送过来的信息,并将这些信息打印,然后关闭所有资源.要先运行服务器程序,然后才能运行客户端程序,当TCP服务器程序运行到Socket.accpet()方法等待客户连接时,accept方法将阻塞,一直到有客户连接请求到来,该方法才会返回,如果又没有请求到来,又没有发生阻塞,通常都是使用了一个已经被占用的端口.
我们可以使用
windows提供的telnet工具在命令行窗口中测试一下服务器程序:命令如下:telnet localhost 8000
可以看到,telnet只要有输入就发送,因此我们如果想要在服务器端一次读多个字符的话,还需要进一步处理,看如下代码:
[java]
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(8000);
Socket s = ss.accept();
InputStream ips = s.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(ips)); //对InputStream进行包装,增加了缓存
OutputStream ops = s.getOutputStream();
ops.write("hello,World!".getBytes());
System.out.println(br.readLine());
br.close(); //关闭包装类,会自动关闭里面的基类
ops.close();
s.close();
ss.close();
}
}
再次使用telnet工具可以看到,这次可以发送不止一个字符了,按回车键后发送数据到服务器端.
2.4,TCP程序例子改进后的服务器程序:
大多数情况下,服务器端都要服务多个客户端,但一次accept方法调用只接收一个连接,因此,要把accept方法放在一个循环语句中,这样就可以接收多个连接.每个连接的数据交换代码也放在一个循环中,这样才能保证两者可以不停地交换数据.
每个连接的数据交换代码必须放在独立的线程中运行,否则,这在段代码运行期间,就没法执行其他的程序代码,accept方法也得不到调用,新的连接无法进入.
下面是一个例子,客户端向服务器发送一个字符串,服务器将这个字符串中的所有字符反向排列后回送给客户端.客户端输入"quit",退出程序.
[java]
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(8000);
while(true){
Socket s = ss.accept();
new Thread(new Servicer(s)).start();
}
}
}
class Servicer implements Runnable{
Socket s;
public Servicer(Socket s){
this.s = s;
}
public void run(){
try{
InputStream ips = s.getInputStream();
OutputStream ops = s.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(ips));