一通过构造方法连接服务器
我们可以通过个重载构造函数以不同的方式来连接服务器这个重载的构造函数可以分为两类
自动选择IP
这种方式是最常用的所谓自动选择IP是指当本机有多块网卡或者在一个网卡上绑定了多个IP时Socket类会自动为我们选择一个可用的IP在上述个构造方法中有个是使用这种方法来连接服务器的
<![if !supportLists]>()
<![endif]>public Socket(String host int port)
这是最常用的构造方法在前面的例子中就是使用的这个构造方法在使用时只需要提供一个字符串类型的IP或域名以及一个整型的端口号即可在这个构造方法中可能会抛出两个错误UnknownHostException和IOException发生第一个错误的原因是我们提供的host并不存在或不合法而其它的错误被归为IO错误因此这个构造方法的完整定义是
public Socket(String host int port) throws UnknownHostException IOException
() public Socket(InetAddress inetaddress int port)
这个构造方法和第一种构造方法类似只是将字符串形式的host改为InetAddress对象类型了在这个构造方法中之所以要使用InetAddress类主要是因为考虑到在程序中可能需要使用Socket类多次连接同一个IP或域名这样使用InetAddress类的效率比较高另外在使用字符串类型的host连接服务器时可能会发生两个错误但使用InetAddress对象来描述host只会发生IOException错误这是因为当你将IP或域名传给InetAddress时InetAddress会自动检查这个IP或域名如果这个IP或域名无效那么InetAddress就会抛出UnknownHostException错误而不会由Socket类的构造方法抛出因此这个构造方法的完整定义是
public Socket(InetAddress inetaddress int port) throws IOException
() public Socket(String host int port boolean stream)
这个构造方法和第一种构造方法差不多只是多了一个boolean类型的stream参数如果这个stream为true那么这个构造方法和第一种构造方法完全一样如果stream为false则使用UDP协议建立一个UDP连接(UDP将在下面的章节详细讨论在这里只要知道它和TCP最大的区别是UDP是面向无连接的而TCP是面向有连接的)也许是当初Sun的开发人员在编写Socket类时还未考虑编写处理UDP连接的DatagramSocket类所以才将建立UDP连接的功能加入到Socket类中不过Sun在后来的JDK中加入了DatagramSocket类所以这个构造方法就没什么用了因此Sun将其设为了Deprecated标记也就是说这个构造方法在以后的JDK版本中可以会被删除其于以上原因在使用Java编写网络程序时尽量不要使用这个构造方法来建立UDP连接
() public Socket(InetAddress inetaddress int port boolean flag)
这个构造方法和第三种构造方法的flag标记的含义一样也是不建议使用的
下面的代码演示上述种构造方法的使用
package mysocket;
import *;
import javaio*;
public class MoreConnection
{
private static void closeSocket(Socket socket)
{
if (socket != null)
try
{
socketclose();
}
catch (Exception e) { }
}
public static void main(String[] args)
{
Socket socket = null socket = null socket = null socket = null;
try
{
// 如果将改成其它不存在的域名
将抛出UnknownHostException错误
// 测试public Socket(String host int port)
socket = new Socket( );
Systemoutprintln(socket连接成功!);
// 测试public Socket(InetAddress inetaddress int port)
socket = new Socket(InetAddressgetByName() );
Systemoutprintln(socket连接成功!);
// 下面的两种建立连接的方式并不建议使用
// 测试public Socket(String host int port boolean stream)
socket = new Socket( false);
Systemoutprintln(socket连接成功!);
// 测试public Socket(InetAddress inetaddress int i boolean flag)
socket = new Socket(InetAddressgetByName() false);
Systemoutprintln(socket连接成功!);
}
catch (UnknownHostException e)
{
Systemoutprintln(UnknownHostException 被抛出!);
}
catch (IOException e)
{
Systemoutprintln(IOException 被抛出!);
}
finally
{
closeSocket(socket);
closeSocket(socket);
closeSocket(socket);
closeSocket(socket);
}
}
}
在上面代码中的最后通过finally关闭了被打开的Socket连接这是一个好习惯因为只有在将关闭Socket连接的代码写在finally里无论是否出错都会执行这些代码但要注意在关闭Socket连接之前必须检查Socket对象是否为null这是因为错误很可能在建立连接时发生这样Socket对象就没有建立成功也就用不着关闭了
手动绑定IP
当本机有多个IP时(这些IP可能是多块网卡上的也可能是一块网卡上绑定的多个IP)在连接服务器时需要由客户端确定需要使用哪个IP这样就必须使用Socket类的另外两个构方法来处理下面让我们来看看这两个构造方法是如何来使用特定的IP来连接服务器的
public Socket(String host int port InetAddress inetaddress int localPort)
这个构造方法的参数分为两部分第一部分为前两个参数host和port它们分别表示要连接的服务器的IP和端口号第二部分为后两个参数inetaddress和localPort其中inetaddress则表示要使用的本地的IP而localPort则表示要绑定的本地端口号这个localPort这以设置为本机的任何未被绑定的端口号如果将localPort的值设为java将在到之间随即选择一个未绑定的端口号因此在一般情况下将localPort设为
public Socket(InetAddress inetaddress int port InetAddress inetaddress int localPort)
这个构造方法和第一个构造方法基本相同只是将第一个参数host换成了inetaddress其它的使用方法和第一个构造方法类似
在下面的代码中将使用这两个构造方法来做一个实验我们假设有两台计算机PC和PCPC和PC各有一块网卡PC绑定有两个IP和PC绑定有一个IPPC和PC的子网掩码都是而PC的默认网关为下面的代码需要在PC上运行
package mysocket;
import *;
public class MoreConnection
{
public static void main(String[] args)
{
try
{
InetAddress localAddress = InetAddressgetByName();
InetAddress localAddress = InetAddressgetByName();
// 如果将localAddress改成localAddresssocket无法连接成功
Socket socket = new Socket( localAddress );
Systemoutprintln(socket连接成功!);
Socket socket = new Socket( localAddress );
Systemoutprintln(socket连接成功!);
// 下面的语句将抛出一个IOException错误
Socket socket = new Socket( localAddress );
Systemoutprintln(socket连接成功!);
socketclose();
socketclose();
socketclose();
}
catch (Exception e)
{
Systemoutprintln(egetMessage());
}
}
}
运行上面代码的输出结果如下
socket连接成功!
socket连接成功!
Connection timed out connect
从上面的输出结果可以看出socket和socket已经连接成功而socket并未连接成功从例程可以看出socket在连接时使用localAddress绑定到了上而PC的IP是因此socket所使用的IP和PC的IP在同一个网段所以socket可以连接成功如果将localAddress改成localAddress后socket将无法连接成功另外两个Socket连接socket和socket是通过Internet连接它们所不同的是socket绑定的是而socket绑定的是它们执行的结果是socket可以连接成功而socket连接失败这是因为socket所绑定的IP和PC的默认网关在同一个网段因此socket可以连接到Internet而socket所绑定的IP和PC的IP不在同一个网段因此socket将无法连接到Internet
二通过connect方法连接服务器
Socket类不仅可以通过构造方法直接连接服务器而且还可以建立未连接的Socket对象并通过connect方法来连接服务器Socket类的connect方法有两个重载形式
public void connect(SocketAddress endpoint) throws IOException
Socket类的connect方法和它的构造方法在描述服务器信息(IP和端口)上有一些差异在connect方法中并未象构造方法中以字符串形式的host和整数形式的port作为参数而是直接将IP和端口封装在了SocketAddress类的子类InetSocketAddress中可按如下形式使用这个connect方法
Socket socket = new Socket();
nnect(new InetSocketAddress(host port));
public void connect(SocketAddress endpoint int timeout) throws IOException
这个connect方法和第一个connect类似只是多了一个timeout参数这个参数表示连接的超时时间单位是毫秒使用timeout设为则使用默认的超时时间
在使用Socket类的构造方法连接服务器时可以直接通过构造方法绑定本地IP而connect方法可以通过Socket类的bind方法来绑定本地IP例程演示如何使用connect方法和bind方法
package mysocket;
import *;
public class MoreConnection
{
public static void main(String[] args)
{
try
{
Socket socket = new Socket();
Socket socket = new Socket();
Socket socket = new Socket();
nnect(new InetSocketAddress( ));
socketclose();
Systemoutprintln(socket连接成功!);
/*
将socket绑定到将产生一个IOException错误
socketbind(new InetSocketAddress( ));
*/
socketbind(new InetSocketAddress( ));
nnect(new InetSocketAddress( ));
socketclose();
Systemoutprintln(socket连接成功!);
socketbind(new InetSocketAddress( ));
nnect(new InetSocketAddress( )
);
socketclose();
Systemoutprintln(socket连接成功!);
}
catch (Exception e)
{
Systemoutprintln(egetMessage());
}
}
}
上面的代码的输出结果为
socket连接成功!
socket连接成功!
Connection timed out connect
在上面代码中的socket连接服务器时为其设置了超时时间(毫秒)因此socket在非常短的时间就抛出了IOException错误