Socket通信详细解析(附例子)

什么是Socket?

Socket,又叫套接字,是一种软件形式的抽象,用于表达两台机器间一个连接的“终端”。服务端的socket在服务端机器上特定的端口进行等待,客户端的socket绑定了服务端的IP地址以及它正在监听着的端口,所以两者通过IP和端口连接起来,就如同一条虚拟的通道。可以理解为我们现实中打电话的过程,当没有人打电话给你时,你的手机是处于监听等待的状态,一旦有人拨打了你的号码,就会收到,并接通。


Socket处在网络通信中的什么位置?

在同一台计算机,进程之间可以这样通信,如果是不同的计算机呢?网络上不同的计算机,也可以通信,那么就得使用网络套接字(socket)。socket就是在不同计算机之间进行通信的一个抽象。他工作于TCP/IP协议中应用层和传输层之间的一个抽象。如下图:





下面通过一个一对一聊天的实例来理解Socket的通信过程:


具体思路如下:
1.创建一个服务端
2.创建服务端的两个线程,分别用于读客户端发来的消息和用于写消息给客户端
3.创建一个客户端
4.创建客户端的两个线程,分别用于读服务端发来的消息和用于写消息给服务端
5.客户端和服务端均通过DataOutputStream进行写入操作,通过DataInputStream进行读取操作



工程目录:



代码如下:

服务端主程序:

public class Server{
	public static void main(String[] args){
    		try{
    			//设定服务端在8888端口进行等待
    			ServerSocket s = new ServerSocket(8888);
    			Socket s1 = s.accept();//sever等待链接
			System.out.println("服务器已开启...");
      
    			//实例化输出流
    			DataOutputStream dos = new DataOutputStream(s1.getOutputStream());
    			//实例化输入流
    			DataInputStream dis = new DataInputStream(s1.getInputStream());
    			//实例化两个线程的对象
    			Thread msr = new ServerReadThread(dis);
    			Thread msw = new ServerWriteThread(dos);
    			//启动线程
    			msr.start();
    			msw.start();
    		}
    		//捕获异常
    		catch(SocketException e){
      			System.out.println(e);
    		}catch(IOException e){
      			System.out.println(e);
   		}
  	}
}



服务端读取消息的进程:

public class ServerReadThread extends Thread{
	
	private DataInputStream stream;
	
	public ServerReadThread(DataInputStream stream){
		this.stream = stream;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		String msg;
		while(true){
			try {
				msg = stream.readUTF();//读取客户端的消息
				System.out.println("客户端:"+msg);   //打印客户端的消息
				//如果接收到对方发来的为q的消息,则退出程序
				if(msg.equals("q")){
			        	System.out.println("对方下线,程序退出");
			        	System.exit(0);
			    	}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}
}




服务端发送消息的进程:

public class ServerWriteThread extends Thread{
	
	private DataOutputStream stream;
	
	public ServerWriteThread(DataOutputStream stream) {
		// TODO Auto-generated constructor stub
		this.stream = stream;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		Scanner scanner = new Scanner(System.in);
		String msg;
		try{
		      	while(true){
		    		msg = scanner.nextLine();
		        	stream.writeUTF(msg);
				//如果输入q,则退出程序
 				if(msg.equals("q")){
				        System.out.println("主动下线,程序退出");
				        System.exit(0);
				}
		     	}
		}catch(IOException e){
		        System.out.println(e);
		}
	}

}




客户端主程序:

public class Client{
	public static void main(String[] args){
    		try{
    			//设定服务端在8888端口进行等待
    			Socket s = new Socket("127.0.0.1",8888);
      
    			//实例化输出流
    			DataOutputStream dos = new DataOutputStream(s.getOutputStream());
    			//实例化输入流
    			DataInputStream dis = new DataInputStream(s.getInputStream());
    			//实例化两个线程的对象
    			Thread msr = new ServerReadThread(dis);
    			Thread msw = new ServerWriteThread(dos);
    			//启动线程
    			msr.start();
    			msw.start();
    		}
    		//捕获异常
    		catch(SocketException e){
      			System.out.println(e);
    		}catch(IOException e){
      			System.out.println(e);
   		}
  	}
}




客户端读取消息的进程:

public class ClientReadThread extends Thread{
	
	private DataInputStream stream;
	
	public ClientReadThread(DataInputStream stream){
		this.stream = stream;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		String msg;
		while(true){
			try {
				msg = stream.readUTF();  //读取来自服务端的消息
				System.out.println("服务器:"+msg);  //打印
				//如果接收到对方发来的为q的消息,则退出程序
				if(msg.equals("q")){
			        	System.out.println("对方下线,程序退出");
			        	System.exit(0);
			    	}
			} catch (IOException e) { 
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}
}




客户端发送消息的进程:

public class ClientWriteThread extends Thread{
	
	private DataOutputStream stream;
	
	public ClientWriteThread(DataOutputStream stream) {
		// TODO Auto-generated constructor stub
		this.stream = stream;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		String msg;
		Scanner scanner = new Scanner(System.in);
		try{
			while(true){
		   		msg = scanner.nextLine();
		        	stream.writeUTF(msg);//写入消息
				//如果输入q,则退出程序
 				if(msg.equals("q")){
				        System.out.println("主动下线,程序退出");
				        System.exit(0);
				}
		      	}
		}catch(IOException e){
		        System.out.println(e);
		}
	}

}


运行结果:




注意:

服务端和客户端的端口号必须一致,否则会报错:connection refuse
在服务端,通过调用socket的accept()方法,让服务器一直在指定的端口进行等待,一旦客户端发送请求,则建立连接。
在服务端和客户端的read线程中,都通过while(true)死循环来不断从datainputstream流中读取消息,一旦有消息,就打印出来。
服务端和客户端的stream要调用相对应的方法,即readUTF()和writeUTF()



短连接与长连接:

短连接:客户端发起请求,服务端接收请求,双方建立连接,读写一次之后,就主动close,优点是:管理起来比较简单,存在的连接都是有用的连接,不需要额外的控制手段。适用于多个客户端对应一个服务端的情况。

长连接:客户端发起请求,服务端接收请求,双方建立连接,但是在读写一次之后并不会主动关闭,而是一直循环监听着是否有消息。长连接存在一个不足,就是但客户端变得多了,服务端会承受不住,所以需要有额外的手段来控制,适用于一个客户端对应另外一个端的情况,即点对点。





Socket与http的区别:

HTTP连接使用的是“请求—响应”的方式,HTTP在每次请求结束后都会主动释放连接,是一种“短连接”,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据,而socket则是一旦连接之后,除非双方有一方断开了,否则连接一直存在。
举个例子:假如服务器端要向客户端主动推送消息,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求后才能将数据传回给客户端。


  • 7
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值