一对一的Socket C/S通信
TCP是一种可靠地、基于连接的网络协议。当两台主机准备进行交谈时,都必须建立一个Socket,其中一方作为服务器打开一个Socket并侦听来自网络的连接请求,另一方作为客户,它向网络上的服务器发送请求,通过Socket与服务器传递消息,要建立连接,只需要指定主机的IP地址和端口号即可。
TCP协议通信的服务方实现
服务程序工作在服务器的某个端口上,一旦启动服务,它将在这个端口上侦听,等待客户程序发来的请求。服务器的套接字用服务器套接字类(ServerSocket)来建立。假设服务器工作在端口8000上,以下命令:
SeverSocket sever = new SeverSocket(8000);
建立了一个服务者sever,它监视端口8000。命令
Socket soc = sever.accept();
让服务者永远等待,直到客户连接到该端口上,一旦有客户送来正确的请求,连接到该端口accept()方法就返回一个Socket对象,表示已经建立好连接,可用Socket的队形soc获得一个输入/输出流,如下:
DataInputStream in = new DataInputStream(soc.getInputStream());
PrintStream out = new PrintStream(soc.getOutputStream());
这里创建了数据输入流类的实例in和输出流类的实例out,是服务者用于从客户接搜输入信息和向客户程序发送信息所用,同样在客户端,也应该建立者两个实例,用来与服务程序进行双向通信。服务者向输入流发送的所用信息都成为客户的输入信息,而客户程序的输出都送入服务者的输入流。
获取主机的IP地址,并在服务者窗口中显示客户机的地址信息:
clientIP = soc.getInetAddress();
System.out.println(“Client’s IP address: ”+clientIP);
println()是输出流类的一个方法,下行向客户送一句问候:
out.println(“Wlcome!...”);
当用远程登录通过端口8000连接到该服务者时,客户终端屏幕上将接受到上述信息。
该简单服务中,每次只读入一行客户的输入,并回显该行,以表明服务者接受了客户的输入。
readLine()是数据输入流类中的一个方法,用于服务者或客户从对方读入一个输入流信息:
String str = in.readLine();
While(!str.equals(“quit”)
{
System.out.println(“Client said:” +str());
str=in.readLine();
}
不断循环以上代码,知道客户输入“quit”或者str为null为止;最后在退出前,关闭输入Socket:
System.out.println(“Client want to leave”);
finally{
in.close();
out.close();
soc.close();
server.close();
}
这就是一个简单的回应服务者的工作过程。每一个服务者,如HTTP Web服务者,都是不停地执行下列循环:
(1)通过输入流向客户获得命令;
(2)以某中方式获取该信息;
(3)通过输出流将信息送给给客户。
TCP协议通信的客户方式实现
客户机先创建一个指向固定主机的固定端口的Socket,加入上述服务者程序在本机“localhost”上,则以下命令:
Socket soc = new Socket(“localhost”,8000);
/*
*三种方法获取本机IP地址:
* 1,Socket soc = new Socket(InetAddress.getByName("localhost"),8000);
* 2,Socket soc = new Socket(InetAddress.getByName("127.0.0.1"),8000);本机回路地址
* 3,Socket soc = new Socket(InetAddress.getByName(null),8000);
*/
建立了客户到服务者的连接,两端进行通信的通路即建立。当服务者接受该连接请求时,Socket实例soc即建立,同样,从该Socket实例中获取输入和输出流:
in = new DataInputStream(sever.getInputStream());
out = new PrintStream(sever.getOutputStream());
输入/输出流建立后,客户首先从客户奇读取发来的“Welcome!...”信息,显示在窗口中:
strin = in.readLine();
System.out.println(“Sever said:”+strin);
这两行命令执行后,窗口中应显示出服务者的欢迎信息和客户机系统输出的信息。
客户想服务者发送的数据流从键盘获得:
sysin = new DataInputStream(System.in);
strout = sysin.readLine();
当键盘输入不是“quit”时,将键盘输入的数据写入输出流中,并发送出去,然后继续从键盘后去输入数据:
out.println(strout);
strout = sysin.readLine();
不断循环上述两行命令,直到键盘输入“quit”后,现将其传送给服务者,然后关闭输入/输出流和Socket:
out.println(strout);
in.close();
out.close();
sysin.close();
server.close();
该客户是与同一台主机上的回应服务者进行通信,先进行服务程序,再运行客户程序,运行结果如下:
在服务进程窗口中显示:
Client’s IP address:127.0.0.1
Client said:Hello!
Client want to leave.
在客户进程窗口中显示:
Connecting to the Server...
Sever said:Welcome!...
Hello!
quit
其中后面三行是客户的键盘输入。如果要在网络中的两台不同计算机之间进行通信,秩序将Socket()中传递的参数“localhost”改成相应的主机名或者IP即可。
源代码:
import java.io.DataInputStream; import java.io.IOException; import java.io.PrintStream; import java.net.Inet4Address; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; public class Sever { static public void main(String args[]) throws IOException { ServerSocket sersoc = null; Socket soc = null; DataInputStream in = null; PrintStream out = null; InetAddress clientIp = null; String str = null; try { sersoc = new ServerSocket(8000); soc = sersoc.accept(); in = new DataInputStream(soc.getInputStream()); out = new PrintStream(soc.getOutputStream()); clientIp = soc.getInetAddress(); System.out.println("Client's IP address: "+clientIp); out.println("Welcome!..."); str = in.readLine(); while(!str.equals("quit")) { System.out.println("Client said: "+str); str = in.readLine(); } System.out.println("Client want to leave."); } catch (Exception e) { System.out.println("Error: "+e); } finally { in.close(); out.close(); soc.close(); sersoc.close(); System.exit(0); } } }
import java.io.DataInputStream; import java.io.IOException; import java.io.PrintStream; import java.net.Socket; public class Client { static public void main(String args[]) throws IOException { Socket soc = null; DataInputStream in = null; PrintStream out = null; DataInputStream sysin = null; String strin = null; String strout = null; try { soc = new Socket(args[0],8000); System.out.println("Connecting to the Server..."); in = new DataInputStream(soc.getInputStream()); out = new PrintStream(soc.getOutputStream()); strin = in.readLine(); System.out.println("Sever said: "+strin); sysin = new DataInputStream(System.in); strout = sysin.readLine(); while (!strout.equals("quit")) { out.println(strout); strout = sysin.readLine(); } out.println(strout); } catch (Exception e) { // TODO: handle exception System.out.println("Error: "+e); } finally { in.close(); out.close(); sysin.close(); soc.close(); System.exit(0); } } }
API注释:套接口类(java.net.Socket)
1)Socket(String host,int port)throws UnknownHostException,IOException
创建一个流套接口(即Socket实体对象),并将其连接至特定主机的特定端口上。
参数: host 主机名
port 端口号
2)Socket(String host,int port,boolean stream)
构造一个套接口(即Socket实体对象),并把它连接到特定主机的特定端口上。而此套接口是流套接口还是数据报(datagram)套接口,则是由最后一个参数stream决定。
参数: host 主机名
port 端口号
stream 用于决定生成的套接口是流套接口还是数据报套接口 (datagram socket)
3)Socket(InetAddress address,int port)
构造一个套接口(即Socket实体对象),并把它连接到特定主机的特定地址上。参数: address 特定的地址
port 端口
4)Socket(InetAddress address,int port,boolean stream)
构造一个流套接口(即Socket实体对象),并把它连接到特定端口的特定地址上。而此套接口是流套接口还是数据报(datagram)套接口,则是有最后一个参数stream决定的。
参数: host 主机名
port 端口号
stream 用于决定生成的套接口是流套接口还是数据报套接口 (datagram socket)
5)InetAddress getInetAddress()
返回该套接口(socket)所连接的地址。
6)int getPort()
返回该套接口(socket)所连接的远程端口。
7)synchronized void close()throws IOException
关闭套接口。
8)InputStream getInputStream()throws IOException
获得从套接口读入数据的输入流。
注:DataInputStream是InputStream的子类
9)OutputStream getOutputStream() throws IOException
获得向套接口进行读操作的输入流。
注:PrintStream为OutputStream的子类。
API注释:服务者套接口类(java.net.ServerSocket)
1)ServerSocket(int port)throws IOException
在指定的端口上构造一个服务者套接口,即构造一个ServerSocket实体对象。
参数: port 端口号
2)ServerSocket(int port,int count)
构造一个服务者套接口,即构造一个ServerSocket实体对象,并且该对象时与指定的当地端口相连接的。此外,可以对它进项监听。用户也可以通过将port设置为0来将该对象与一个匿名端口相连接。
参数: port 端口号
count 对该ServerSocket实体对象与端口间的连接进行监听的 次数。
3)Socket accept()throws IOException
等待一个连接,该方法将阻塞当前线程,知道连接成功。该方法返回一个套接口类(Socket)对象,通过该对象,程序与链接的客户进行通信。
4)void close()throws IOException
关闭套接口。