Socket是网络上运行的两个程序间双向通信的一端,它既可以接受请求,也可以发送请求。
利用它可以较为方便的实现网络上数据的传递。在JAVA中,有专门的socket类来处理用户的请求和相应。
Socket有两种主要的操作方式:面向连接的和无连接的。
面向连接的socket操作就像是一部电话,他们必须建立一个连接和呼叫。它使用的TCP协议,
在这个模式下建立的socket连接必须在发送数据之前与目的地的socket取得一个连接。一旦连接建立,
socket就可以使用一个流:打开->读->写->关闭。所有发送的信息都会在另一端以同样的顺序被接收。
面向连接的操作比无连接的操作效率更低,但是数据的安全性更高。
无连接的socket操作就像是一个邮件投递,没有什么保证,多个邮件可能到达的时间顺序和出发顺序不同。
它使用的是数据报(UDP)协议。这个模式下的socket不需要连接一个目的地的socket,它只是简单的透出数据报。
无连接的操作是快速和高效的,但是数据安全性不佳。
两种传输协议的比较:
UDP:1,每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。
2,UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。
3,UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方
TCP:1,面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中需要连接
时间。
2,TCP传输数据大小限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大的
数据。
3,TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。
Socket通讯的过程
Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。
一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。
对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:
(1) 创建Socket;
(2) 打开连接到Socket的输入/出流;
(3) 按照一定的协议对Socket进行读/写操作;
(4) 关闭Socket.(在实际应用中,并未使用到显示的close,虽然很多文章都推荐如此,不过在我的程序中,可能因为程序本身比较简单,要求不高,所以并未造成什么影响。)
简单socket实例:
服务端:
package com.htxx.action.JAVA;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketServer {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
ServerSocket serverSocket=null;
//用于向客户端打印输出
PrintWriter writer=null;
try {
//实例化一个服务器端的socket连接
serverSocket=new ServerSocket(9999);
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("could not listen on port:9999");
System.exit(1);
}
Socket clientSocket=null;
try {
//accept方法用来监听客户端连接
//accept方法用于接收客户端的socket请求
clientSocket=serverSocket.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("accpet failed");
System.exit(1);
}
//通过客户端的socket对象去实例化printwriter对象,
//此时writer就具备了向客户端打印信息的能力
writer=new PrintWriter(clientSocket.getOutputStream(), true);
//打印信息至客户端
writer.println("hello world");
clientSocket.close();
serverSocket.close();
//writer.close();
}
}
客户端:
package com.htxx.action.JAVA;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
public class SocketClient {
public static void main(String[] args) throws IOException {
Socket socketClient=null;
//用于读取服务器端发送过来的数据
BufferedReader reader=null;
//输入输出流与socket关联
try {
socketClient=new Socket("localhost", 9999);
reader=new BufferedReader(
new InputStreamReader(socketClient.getInputStream()));
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
System.out.println("unkown host");
System.exit(1);
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("i/o exception");
System.exit(1);
}
System.out.println(reader.readLine());
reader.close();
socketClient.close();
}
}
服务端启动后,等待客户端连接,客户端连接服务端,服务端发送消息给客户端。
可对话的Socket:
服务端:
package com.htxx.action.JAVA;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
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) {
// TODO Auto-generated method stub
ServerSocket serverSocket=null;
PrintWriter out=null;
BufferedReader in=null;
try {
serverSocket=new ServerSocket(9999);
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("could not listen on port:9999");
System.exit(1);
}
Socket incoming=null;
while(true){
try {
//使用accept()z阻塞等待客户请求,有客户请求到来
//则产生一个socket对象,并继续执行
incoming=serverSocket.accept();
//
out=new PrintWriter(incoming.getOutputStream(), true);
//将字节流放入字符缓冲流当中
in=new BufferedReader(
new InputStreamReader(incoming.getInputStream()));
//提示信息
//out.println("hello~~");
out.println("这里是服务端~~");
//out.flush();
//在没有异常的情况下不断循环
while(true){
//只有当用户输入数据的时候才返回数据内容
String str=in.readLine();
//当用户断开连接时会返回空null
if(str==null){
//退出循环
break;
}else{
//退出命令
if(str.trim().equalsIgnoreCase("BYE")){
break;
}
//控制台输入响应内容
System.out.println("客户端:"+str);
Scanner scanner=new Scanner(System.in);
String reponse=scanner.nextLine();
//向客户端输出内容
out.println(reponse);
//刷新输出流,使client马上收到该字符串
out.flush();
System.out.println("我:"+reponse);
}
}
out.close();
in.close();
incoming.close();
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
客户
端:
package com.htxx.action.JAVA;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import com.microsoft.schemas.vml.STTrueFalse;
public class EchoClient {
public static void main(String[] args) throws IOException {
Socket socketClient=null;
//用于读取服务器端发送过来的数据
BufferedReader in=null;
//发送消息到服务端
PrintWriter out=null;
//输入输出流与socket关联
try {
//向本机的9999端口发出客户请求
socketClient=new Socket("localhost", 9999);
in=new BufferedReader(
new InputStreamReader(socketClient.getInputStream()));
out=new PrintWriter(socketClient.getOutputStream(),true);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
System.out.println("unkown host");
System.exit(1);
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("i/o exception");
System.exit(1);
}
//从控制台输入内容
while(true){
String str=in.readLine();
if(str==null){
break;
}else{
if(str.equalsIgnoreCase("BYE")){
break;
}else{
System.out.println("服务端:"+str);
Scanner scanner=new Scanner(System.in);
String reponse=scanner.nextLine();
out.println(reponse);
System.out.println("我:"+reponse);
}
}
}
in.close();
out.close();
socketClient.close();
}
}
测试:
先启动服务端,再启动客户端:
客户端发送消息给服务端:
查看服务端:
服务端向客户端发消息:
查看客户端: