Java网络编程--TCP-UDP程序设计(ServerSocket类与Socket类)

一、ServerSocket类和Socket类

 

    ServerSocket类主要用在服务端程序的开发上,用于接受客户端的来凝结请求。

    在服务器端每次运行时都要使用accept()方法等待客户端连接,此方法执行之后服务器端将进入阻塞状态,直到客户端连接之后程序才可以向下继续执行。此方法的返回值类型是Socket,每一个Socket都表示一个客户端对象。

ServerSocket常用的方法

    //创建ServerSocket实例,并指定监听端口(构造方法)   
     public ServerSocket(int port) throws IOException {
        this(port, 50, null);
    }

    //等待客户端连接,此方法连接之前一直阻塞
    public Socket accept() throws IOException {
        if (isClosed())
            throw new SocketException("Socket is closed");
        if (!isBound())
            throw new SocketException("Socket is not bound yet");
        Socket s = new Socket((SocketImpl) null);
        implAccept(s);
        return s;
    }
    
    
     //返回服务器的IP地址
     public InetAddress getInetAddress() {
        if (!isBound())
            return null;
        try {
            InetAddress in = getImpl().getInetAddress();
            SecurityManager sm = System.getSecurityManager();
            if (sm != null)
                sm.checkConnect(in.getHostAddress(), -1);
            return in;
        } catch (SecurityException e) {
            return InetAddress.getLoopbackAddress();
        } catch (SocketException e) {
            // nothing
            // If we're bound, the impl has been created
            // so we shouldn't get here
        }
        return null;
    }
    
    //返回ServerSocket的关闭状态
    public boolean isClosed() {
        synchronized(closeLock) {
            return closed;
        }
    }

    //关闭ServerSocket
    public void close() throws IOException {
        synchronized(closeLock) {
            if (isClosed())
                return;
            if (created)
                impl.close();
            closed = true;
        }
    }

 

    在客户端,程序可以通过Socket类的getInputStream()方法取得服务器的输出信息,在服务器端可以通过getOutputStream()方法取得客户端的输出信息。

    网络程序中要使用输入及输出流的形式完成信息的传递,多以在开发时需要导入java.io包。

//接口实现
public
class Socket implements java.io.Closeable

Socket类的常用方法:

    //构造Socket对象,捅死指定要连接服务器的主机名称及连接端口
    public Socket(String host, int port)
        throws UnknownHostException, IOException
    {
        this(host != null ? new InetSocketAddress(host, port) :
             new InetSocketAddress(InetAddress.getByName(null), port),
             (SocketAddress) null, true);
    }

    //返回此套接字的输入流
    public InputStream getInputStream() throws IOException{...}


    //返回此套接字的输出流
    public OutputStream getOutputStream() throws IOException{...}
    //关闭此Socket
    public void close() throws IOException


    //判断此套接字是否被关闭
    public boolean isClosed()

 

二、第一个TCP程序

 

下面通过ServerSocket类及Socket类完成一个服务器的程序开发,此服务器向客户端输出"Hello world"的字符串信息

服务器端:

import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 网络编程----TCP程序---服务器端
 * 
 * @author Mona
 *
 */
public class HelloServer {
	public static void main(String[] args) throws Exception {
		ServerSocket server = null;
		Socket client = null;
		PrintStream out = null;
		server = new ServerSocket(8888);

		System.out.println("服务器运行,等待客户端连接...");
		client = server.accept();// 一个Socket对象表示一个客户端
		String str = "Hello world";
		out = new PrintStream(client.getOutputStream());// 实例化打印流对象,以向客户端输出

		out.println(str);
		out.close();// 关闭打印流
		client.close();// 关闭客户端连接
		server.close();// 关闭服务器连接
	}
}

运行结果:

 

 

从程序的运行结果中可以发现,服务器程序一执行到accept()方法后,程序进入到阻塞状态,此阻塞状态会在客户端连接之后改变。

客户端:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * 网络编程----TCP程序--客户端
 * 
 * @author Mona
 *
 */
public class HelloClient {
	public static void main(String[] args) throws Exception{
		Socket client = null;	//声明Socket对象
		client = new Socket("localhost", 8888);	//指定连接的主机和端口
		BufferedReader buf = null;	//声明BufferedReader对象,接收信息
		buf = new BufferedReader(
				new InputStreamReader(client.getInputStream()));	//取得客户端的输入流
		
		String str = buf.readLine();	//读取信息
		System.out.println("服务器输出内容为:" + str);
		client.close();		//关闭Socket
		buf.close();		//关闭输入流
	}
}

运行结果:

此时客户端从服务器端将信息读取进来,读取完毕后,因为服务器端此时只能处理一次连接请求,所以也将关闭。

 

三、案例:Echo 程序

 

 

      Echo程序是一个网络编程通信交互的一个经典案例,称为回应程序,即客户瑞输人哪些内容,服务器端会在这些内容前加上“ECHO:”并将信息发回给客户端,下面就完成这样的一个程序。
      之前的程序代码,服务器端每次执行完毕后服务器都会退出,这是因为服务器端只能接收一个客户端的连接,主要是由于accept()方法只能使用一次。本程序中将通过循环的方式使用acept这样,每一个客户端执行完毕后,服务器端都可以重新等待用户连接。

服务器端:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * ECHO-----服务器端
 * 
 * @author Mona
 *
 */
public class EchoServer {
	public static void main(String[] args) throws Exception{
		ServerSocket server = null;
		Socket client = null;
		PrintStream out = null;
		BufferedReader buf = null;
		server = new ServerSocket(8888);
		boolean f = true;  //定义一个标记位
		
		while(f) {		//无限制接受客户端连接
			System.out.println("服务器运行,等待客户端连接...");
			client = server.accept();	//接收客户端连接
			buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
			out = new PrintStream(client.getOutputStream());  //实例化客户端的输出流
			
			boolean flag = true;  //标志位,表示一个客户端是否操作完毕
			
			while(flag) {
				String str = buf.readLine();  //不断接收信息
				if(str == null || "".equals(str)) {	//客户端操作结束
					flag = false;
				}else if("bye".equals(str)){	
					flag = false;
				}else {
					out.println("ECHO:" + str);
				}
			}
			out.close();
			client.close();
		}
		server.close();
	}
}

客户端:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

/**
 * 
 * ECHO----客户端
 * 
 * @author Mona
 *
 */
public class EchoClient {
	public static void main(String[] args) throws Exception{
		Socket client = new Socket("localhost", 8888);	//声明Socket对象,指定连接的主机和端口
		BufferedReader buf = null;
		PrintStream out = null;
		BufferedReader input = null;	//声明BufferedReader对象
		input = new BufferedReader(
				new InputStreamReader(System.in));  //从键盘接收数据
		out = new PrintStream(client.getOutputStream());  //实例化客户端的输出流
		buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
		boolean flag = true;  //定义一个标记位
		
		while(flag) {
			System.out.println("输入信息:");
			String str = input.readLine();
			out.println(str);
			
			if("bye".equals(str)){	
				flag = false;
			}else {
				String echo = buf.readLine();	//接收ECHO信息
				System.out.println(echo);
			}
		}
		client.close();
		buf.close();
	}
}

运行结果:

 

从程序的运行结果中可以发现,所有的输入信息最终都会通过回显的方式发回给客户端,并且前面加上了“ECHO"的信息。另外在本程序中,当一个客户端结束之后,服务器端并不会退出,会等待下一个用户连接,继续执行。但在本程序中同时存在一个严重的问题,即现在的服务器端每次只能有一个用户连接,属于单线程的处理机制。此时无法连接其他客户端。

 

四、在服务器上应用多线程

 

 

对于服务器端来说,如果要加入多线程机制,则应该在每个用户连接之后启动一个新的线程。所以下面先建立一个EchoThread类,此类专门用于处理多线程操作,此时的多线程使用Runnable接口实现。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

/**
 * 
 * 在网络服务器上应用多线程
 * 
 * @author Mona
 *
 */
public class EchoThread implements Runnable{
	private Socket client = null;	//接收客户端
	
	public EchoThread(Socket client) {	//通过构造方法设置Socket
		this.client = client;
	}
	
	public void run(){
		PrintStream out = null;
		BufferedReader buf = null;	//用于接收客户端发送来的信息
		try {
			buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
			out = new PrintStream(client.getOutputStream());  //实例化客户端的输出流
			boolean flag = true;  //标志位,表示一个客户端是否操作完毕
			while(flag) {
				String str = buf.readLine();  //不断接收信息
				if(str == null || "".equals(str)) {	//客户端操作结束
					flag = false;
				}else if("bye".equals(str)){	
					flag = false;
				}else {
					out.println("ECHO:" + str);
				}
			}
			out.close();
			client.close();
			
		} catch (Exception e) {
		}
	}
}

 

从本线程类中可以发现,上述程序的 主要功能是接收每一个客户端的Socket, 并通过循环的方式接收客户端的输入信息及向客户端输出信息。下面编写EchoThreadServer 类,并使用以上的EchoThread类。

import java.net.ServerSocket;
import java.net.Socket;

/**
 * 多线程---EchoThreadServer
 * 
 * @author Mona
 *
 */
public class EchoThreadServer {
	public static void main(String[] args) throws Exception{
		ServerSocket server = null;
		Socket client = null;
		server = new ServerSocket(8888);
		boolean f = true;  //定义一个标记位
		
		while(f) {
			System.out.println("服务器运行,等待客户端连接...");
			client = server.accept();	//接收客户端连接
			new Thread(new EchoThread(client)).start();
		}
		server.close();
	}
}

在服务器端,每一个连接服务器端Socket都会以一个线程的方式预计女性,这样无论有多少个客户端连接都可以同时完成操作。

 

五、UDP程序设计

 

 

      TCP的所有操作都必须建立可靠的连接,这样肯定会浪费大量的系统性能。为了减少这种开销, 在网络中又提供了 另外一种传输协议UDP,是不可靠的连接, 这种协议在各种聊天工具中被广泛地应用。
      使用UDP发送的信息,对方不一定 会接收到。所有的信息使用数据报的形式发送出去,所以这就要求客户端要始终等待服务器发送的消息才能进行接收,在Java 中使用DatagramSocket类和DatagramPacket类完成UDP程序的开发。

(提示:关于UDP开发中服务器和客户端的解释。使用UDP开发的网络程序类似于平常使用的手机,手机实际上相当于一个客户端,如果手机要想正常地接收到信息,则手机肯定要先开机才行。)

在UDP开发中使用DatagramPacket类包装一条要发送的信息,之后使用DatagramSocket类用于完成信息的发送操作。

要想实现UDP程序,则首先应该从客户端编写,在客户端指定要接收数据的端口和取得数据。

 

UDP客户端--UDPClient:

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * UDP客户端--UDPClient
 * 
 * @author Mona
 *
 */
public class UDPClient {
	public static void main(String[] args) throws Exception{
		DatagramSocket ds = null;
		byte[] buf = new byte[1024];	//定义接收数据的字节数组
		DatagramPacket dp = null;
		ds = new DatagramSocket(9000);
		dp = new DatagramPacket(buf, 1024);	//指定接收数据的长度为1024
		System.out.println("等待接收数据...");
		ds.receive(dp);	//接收数据
		String str = new String(dp.getData(),0,dp.getLength()) 
				+ " from" + dp.getAddress().getHostAddress()
				+ " : " + dp.getPort();	//接收数据
		System.out.println(str);
		ds.close();
	}
}

运行结果:

编写UDP发送的服务器端程序----UDPServer

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * 编写UDP发送的服务器端程序----UDPServer
 * 
 * @author Mona
 *
 */
public class UDPServer {
	public static void main(String[] args) throws Exception{
		DatagramSocket ds = null;
		DatagramPacket dp = null;
		ds = new DatagramSocket(3000);
		String str = "Hello World";	//准备好要发送的数据
		//实例化DatagramPacket对象,指定数据内容、数据长度、要发送的目标地址、发送端口
		dp = new DatagramPacket(str.getBytes(), str.length(),InetAddress.getByName("localhost"),9000);//此处向客户端发送9000端口
		System.out.println("发送信息...");
		ds.send(dp);		//发送数据报
		ds.close();
	}
}

运行结果:

下面为先执行客户端,接着执行服务器端的运行结果:

客户端:

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值