II 3.1 连接到服务器

II 3.1 连接到服务器

package socket;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Scanner;

public class SocketTest 
{
	public static void main(String[] args) throws IOException
	{
		try(Socket s = new Socket("timr-A.tiemfreq.bldrdoc.gov", 13))
		{
			InputStream inStream = s.getInputStream();
			Scanner in = new Scanner(inStream);
			
			while(in.hasNextLine())
			{
				String line = in.nextLine();
				System.out.println(line);
			}
		}
	}

}
Socket s = new Socket("timr-A.tiemfreq.bldrdoc.gov", 13);
InputStream inStream = s.getInputStream();

  第一行用来打开一个套接字,是一个抽象概念,用来负责启动程序内部和外部之间的通信。将远程地址和端口号传递给套接字的构造器,如果连接失败,会跑抛出一个UnknowHostException异常;如果存在其他问题,会抛出IOException异常。由于UnknowHostException是IOException的子类,所以在这里只需要捕获超类的异常即可。

  一旦套接字打开,java.net.Socket类中的getInputStream方法就会返回一个InputStream对象,这个对象可以和其他任意流对象一样使用。一旦获取了这个流,程序就会将每一行打印出来,一直持续到流发送完毕。

Socket(String host, int port)

  构建一个套接字,用来连接给定的主机和端口。

InputStream getInputStream()

  获取可以从套接字中读取数据的流。

OutputStream getOutputStream()

  向套接字写出数据的流。

3.1.1 套接字超时

从套接字读取信息的时候,在有数据可以访问之前,读操作将会被阻塞。如果此时主机不可达,那么应用会等到很长的时间,并且因为受低层操作系统的限制最终会导致超时。

  对于不同的应用,应该确定合理的超时值。然后调用setSoTimeout方法设置这个超时值(单位:毫秒)

Socket s = new Socket(...);
s.setSoTimeout(10000);//10秒钟

  如果已经为套接字设置了超时值,并且之后的读操作和写操作在没有完成之前就超过了时间限制,那么这些操作就会抛出SockTimeoutException异常,可以捕获这个异常,并且对超时做出反应。

try
{
    InputStream in = s.getInputStream();
    ...
}catch(InterruptedIOException exception)
{
    react to timeout
}

  另外还有一个超时问题是必须解决的,下面的这个构造器:

Socket(String host, int port)

  会一直无限期地阻塞下去,直到建立了主机之间的初始连接为止。

  可以通过先构造一个无连接的套接字,然后再使用一个超时来进行连接的方法解决这个问题。

Socket s = new Socket();
s.connect(new InputSocketAddress(host, port), timeout);

 用到的API如下:

Socket()

  创建一个未被连接的套接字。

void connect(SocketAddress address)

  将该套接字连接到指定的地址。

void connect(SocketAddress address, int timeoutInMilliseconds)

  将套接字连接到指定的地址,如果在给定的时间里没有响应,则返回。

void setSoTimeout(int timeoutInMillseconds)

  设置该套接字上读请求的阻塞时间。如果超出给定的时间,则抛出一个InterruptedIOException异常。

boolean isConnected()

  如果套接字已经被连接,则返回true。

boolean isClosed()

  如果套接字已经被关闭,则返回true。

3.1.2 因特网地址

通常不需要过多的考虑因特网地址的问题,它们是用一串数字表示的主机地址。一个因特网地址由4个字节组成(IPv6中是16个字节),比如132.3.2.123。但是需要在主机名和因特网地址之间进行转换,那么就可以使用InetAddress类。

  静态的getByName方法可以返回代表某个主机的InetAddress对象。

InetAddress address = InetAddress.getByName("time-A.timefreq.bldrdoc.gov");

  将会返回一个InetAddress对象,这个对象封装了一个4字节的序列:132.3.2.123。然后可以使用getAddress方法来访问这些字节:

byte[] addresses = InetAddress.getAllByName(host);

  一些访问量比较大的主机名通常会对应多个因特网地址,以实现负载均衡,比如goole.com会对应多个因特网地址。当访问主机的时候,会随机的选取其中的一个。可以通过getAllByName来获取所有的主机:

InetAddress[] addresses = InetAddress.getAllByName(host);

  有时候需要本地主机的地址,如果只是要求得到localhost的地址,那么总会得到地址127.0.0.1,但是其他程序无法使用这个地址来连接到这台机器上。此时可以使用静态的getLocalHost方法来得到本地主机的地址:

InetAddress address = InetAddress.getLocalHost();
package socket;

import java.io.IOException;
import java.net.InetAddress;

public class InetAddressTest 
{
	public static void main(String[] args) throws IOException
	{
		if(args.length > 0)
		{
			String host = args[0];
			InetAddress[] address = InetAddress.getAllByName(host);
			for(InetAddress a:address)
			{
				System.out.println(a);
			}
		}
		else
		{
			InetAddress localHostAddress = InetAddress.getLocalHost();
			System.out.println(localHostAddress);
		}
	}
}

3.2 实现服务器

实现一个简单的服务器,它可以向客户端发送信息,一旦启动服务器程序,它便会等待某个用户端连接到它的端口。

ServerSocket s = new ServerSocket(8189);

  用于建立一个负责监控端口8189的服务器。

Socket incoming = s.accept();

  用于高速程序不停等待,直到有客户端连接到这个端口,一旦有人通过网络发送了正确的连接请求,并以此连接到了端口上,该方法就会返回一个表示连接已经建立的Socket对象。

package socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class EchoServer 
{
	public static void main(String[] args) throws IOException
	{
		try(ServerSocket s = new ServerSocket(8189))
		{
			try(Socket incoming = s.accept())
			{
				InputStream inStream = incoming.getInputStream();
				OutputStream outStream = incoming.getOutputStream();
				
				try(Scanner in = new Scanner(inStream))
				{
					PrintWriter out = new PrintWriter(outStream, true);
					out.println("Hello! Enter BYE to exit.");
					
					boolean done = false;
					while(!done && in.hasNextLine())
					{
						String line = in.nextLine();
						out.println("Echo: " + line);
						if(line.trim().equals("BYE"))
							done = true;
					}
				}
			}
		}
	}

}
ServerSocket(int port)

  创建一个监听端口的服务器套接字。

Socket accept()

  等待连接。

  该方法阻塞当前进程直到建立连接为止。这个方法返回一个Socket对象,程序可以通过这个对象与连接中的客户端进行通信。

void close()

  关闭服务器套接字。

3.2.1 为多个客户端服务

服务器总是不间断地运行在服务器计算机上,来自整个因特网的用户希望同时使用服务器。前面介绍的服务器会拒绝多客户端连接,使得某个用户可能会因长时间地连接服务器而独占服务,所以需要使用多线程。

  每当程序建立一个新的套接字连接,也就是调用accept的时候,将会启动一个新的线程来处理服务器和该客户端之间的连接,而主程序将立刻返回并等待下一个连接。

package socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class ThreadEchoServer 
{
	public static void main(String[] args)
	{
		try
		{
			int i = 1;
			ServerSocket s = new ServerSocket();
			while(true)
			{
				Socket incoming = s.accept();
				System.out.println("Spawning" + i);
				Runnable r = new ThreadEchoHandler(incoming);
				Thread t = new Thread(r);
				t.start();
				i++;
			}
		}catch(IOException e)
		{
			e.printStackTrace();
		}
	}
}	

class ThreadEchoHandler implements Runnable
	{
		private Socket incoming;
		public ThreadEchoHandler(Socket i)
		{
			incoming = i;
		}	
		
		public void run()
		{
			try
			{
				try
				{
					//
					InputStream inStream = incoming.getInputStream();
					OutputStream outStream = incoming.getOutputStream();
					
					Scanner in = new Scanner(inStream);
					//
					PrintWriter out = new PrintWriter(outStream, true);
					out.println("Hello! Enter BYE to exit");
					boolean done = false;
					while(!done && in.hasNextLine())
					{
						String line = in.nextLine();
						out.println("Echo: " + line);
						if(line.trim().equals("BYE"))
							done = true;
					}
				}
				finally
				{
					incoming.close();
				}
			}catch(IOException e)
			{
				e.printStackTrace();
			}
		}
	}

3.2.2 半关闭

半关闭(half-close)提供这样的一种能力:套接字连接的一端可以终止其输入,同时仍旧可以接收来自另一端的数据。

3.3 可中断套接字

3.4 获取Web数

3.4.1 URL和URI

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏张学林的专栏

手 Q 红包工程师过去一年踩过的坑

工作中遇到的一切,几乎都是从0开始,故难免会走很多弯路,也曾踩过无数的坑,欢迎各位解答。若有异议,欢迎拍砖。

37500
来自专栏jeremy的技术点滴

sed命令工作原理及命令备忘

35090
来自专栏Linux驱动

第1阶段——uboot分析之启动函数bootm命令 (9)

本节主要学习: 详细分析UBOOT中"bootcmd=nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0...

19450
来自专栏jeremy的技术点滴

现代Web开发系列教程_06

32570
来自专栏移动开发的那些事儿

BlockCanary源码解析

如上代码中的loop()方法是Looper中的,我们的目的是监测主线程的卡顿问题,因为UI更新界面都是在主线程中进行的,所以在主线程中做耗时操作可能会造成界面卡...

16320
来自专栏Linux驱动

第1阶段——uboot分析之启动函数bootm命令 (9)

本节主要学习: 详细分析UBOOT中"bootcmd=nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0" 中...

28590
来自专栏菩提树下的杨过

Oracle中使用Entity Framework 6.x Code-First方式开发

去年写过一篇EF的简单学习笔记,当时EF还不支持Oracle的Code-First开发模式,今天无意又看了下Oracle官网,发现EF6.X已经支持了,并且给出...

27750
来自专栏有趣的django

37.Django1.11.6文档

第一步 入门 检查版本 python -m django --version 创建第一个项目 django-admin startproject mysite ...

50180
来自专栏Star先生的专栏

从源码中分析 Hadoop 的 RPC 机制

RPC是Remote Procedure Call(远程过程调用)的简称,这一机制都要面对两个问题:对象调用方式余与序列/反序列化机制。本文给大家介绍从源码中分...

75100
来自专栏熊二哥

快速入门系列--WebAPI--04在老版本MVC4下的调整

WebAPI是建立在MVC和WCF的基础上的,原来微软老是喜欢封装的很多,这次终于愿意将http编程模型的相关细节暴露给我们了。在之前的介绍中,基本上都基于.N...

24760

扫码关注云+社区

领取腾讯云代金券