什么是套接字
Socket即套接字(插座),是一种软件形式的抽象,表示两台机器间一个连接的“终端”。想象两台机器间的连接是一条虚拟的“线缆”,线缆的每一段都插入对应机器的“插座”或者“套接字”里。Socket让我们尽可能不必知道连接的细节,而直接使用它来建立连接。
java中的套接字
对于TCP连接有两个基本的套接字类,ServerSocket和Socket。客户端用Socket初始一个连接,服务端用ServerSocket中的accept方法“侦听”连接,侦听到了就返回一个Socket,从此得到“Socket-Socket”连接,之后可以用同样的方式对待连接的两端。此时用Socket中getInputStream()、getOutputStream()方法产生对应的InputStream和OutputStream对象(这些数据流必须封装到缓冲区内),再使用转换器转换为Reader和Writer。
简单TCP客户端程序与TCP服务端程序交互
TCP服务端的工作:
1.创建一个ServerSocket类,调用accept方法等待建立一个连接
2.用那个连接产生的Socket创建一个InputStream以及OutputStream
3.将InputStream中读到的内容反馈给outputStream,直到收到“END”内容时停止,关闭连接
TCP服务端代码如下:
public class TcpServer {
public static final int PORT = 8001;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(PORT);
System.out.println("listening...");
try{
//blocks until a connection occurs
Socket s = ss.accept();
try{
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter out = new PrintWriter(new OutputStreamWriter(s.getOutputStream()),true);
String str = "welcome!";
out.println(str);
while(true){
str = in.readLine();
if(str.equals("END")) break;
System.out.println("Received:" + str);
out.println("Server Received:" + str);
}
//close the tow sockets
} finally{s.close();}
} finally{ss.close();}
}
}
每次写完服务端,都可以用telnet进行测试:telent 127.0.0.1 8001
客户端代码如下:
public class client {
public static void main(String[] args) throws Exception {
Socket s = new Socket("127.0.0.1", 8001);
try{
BufferedReader netin = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
System.out.println(netin.readLine());
while(true)
{
String strLine = keyin.readLine();
out.println(strLine);
if(strLine.equalsIgnoreCase("END"))
{
break;
}
System.out.println(netin.readLine());
}
}finally{
System.out.println("closing...");
s.close();
}
}
}
服务器程序先启动运行,客户程序才能连接上TCP服务器
这里对资源的清除用以下方式:
1.假如ServerSocket构建器失败,则main()掷出一个Exception;
2.假如ServerSocket构建器成功,其他所有方法到try-finally里寻求保护,确保无论发生什么情况,ServerSocket都能正确关闭;
3.Socket同理。若accept()成功,则后续语句必须进入try-finally块,以保障在后续语句失败时,Socket能得到正确清除。
这里的PrintWriter ,它有一个过载的构建器,能获取第二个参数—— 一个布尔值标志,指向是否在每一次 println()结束的时候自动刷新输出(但不适用于 print()语句)。每次写入了输出内容后(写进out),它的缓冲区必须刷新,使信息能正式通过网络传递出去。对目前这个例子来说,刷新显得尤为重要,因为客户和服务器在采取下一步操作之前都要等待一行文本内容的到达。若刷新没有发生,那么信息不会进入网络,除非缓冲区满(溢出),这会为本例带来许多问题。
服务器服务多个客户
客户端代码可以不变
服务器能够与多个客户端的连接请求,需要循环调用ServerSocket.accept方法,得到Socket类;
会话过程不能相互影响,会话要在一个独立的线程运行;一个线程服务与一个服务器端的Socket对象相关联
服务器段代码如下:
public class Servicer implements Runnable {
private Socket s;
BufferedReader in;
PrintWriter out;
public Servicer (Socket s) throws IOException{
this.s = s;
}
public void run() {
try{
in = new BufferedReader(new InputStreamReader(s.getInputStream()));
out = new PrintWriter(s.getOutputStream(),true);
out.println("welcom!");
while(true){
String strLine = in.readLine();
if(strLine.compareToIgnoreCase("END")==0) break;
System.out.println("received:" + strLine);
//将收到的信息翻转顺序,发送给客户端
StringBuffer result = new StringBuffer(strLine).reverse();
out.println(strLine + "---"+result.toString());
}
}catch (Exception e){
e.printStackTrace();
} finally{
out.println("closing connection");
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
以及
public class MultiTcpServer {
static final int PORT = 8003;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(PORT);
System.out.println("Server listening...");
try{
while(true){
Socket s = ss.accept();
try{
new Thread(new Servicer(s)).start();
}catch (Exception e){
s.close();
}
}
}finally {
ss.close();
}
}
}
这里,
Abd{backspace}c 显示:abc 翻转后c{backspace}dba 显示dba