Socket实现数据通信(2)——Socket实现客户端与服务端长连接通讯

        在上一篇中,我们结合Socket的一些基本概念以及最后的一个小示例对其进行了一个基本的介绍

上一篇:Socket实现数据通信(1)——初识Socket    下一篇:Socket实现数据通信(3)——基于DatagramSocket实现服务器与客户端之间简单的通讯

今天,我们就对Socket相关的API以及接口进行一个详细一点的介绍,最后,在实现一个客户端与服务端长连接通讯的一个小例子。示例效果图:

1,Socket相关的部分API

我们在上一篇中,通过下面方式构建了一个Socket对象,一个主机和一个端口号。

Socket client = new Socket("127.0.0.1", 88);

其实,其构造方法远不止这一个,下面就来看看

 Socket()

创建一个未连接的套接字,并使用系统默认类型的SocketImpl。

 Socket(InetAddress address, int port)

创建流套接字并将其连接到指定IP地址的指定端口号。

 Socket(InetAddress host, int port, boolean stream)已弃用

使用DatagramSocket代替UDP传输。

 Socket(InetAddress address, int port, InetAddress localAddr, int localPort)

创建套接字并将其连接到指定的远程端口上指定的远程地址。

 Socket(Proxy proxy)

创建一个未连接的套接字,指定应该使用的代理类型(如果有的话),无论其他任何设置如何。

protected Socket(SocketImpl impl)

使用用户指定的SocketImpl创建一个未连接的Socket。

 Socket(String host, int port)

创建流套接字并将其连接到指定主机上的指定端口号。

 Socket(String host, int port, boolean stream)已弃用

使用DatagramSocket代替UDP传输。

 Socket(String host, int port, InetAddress localAddr, int localPort)

创建套接字并将其连接到指定远程端口上的指定远程主机。

可见,除了通过一个host和一个端口构建Socket之外,还可以通过InetAddress与端口,无参构造等等。除了两个被弃用的之外,其他的可以看做有两类:

  • 连接的套接字
  • 未连接的套接字

当我们知道了要连接服务器的IP(主机)和端口并且准备连接时,可以直接创建前者,如果不知道或者暂时不准备连接可创建后者。关于其接口方法那就更多了,下面介绍几个经常使用的:

close():关闭此套接字

connect():连接到服务器(根据传入的参数,对应上面说的第二种创建Socket的方式)

getInputStream():获取套接字中的输入流

getOutputStream():获取套接字中的输出流

shutDownInput():输入流放置在流的末尾(停止输入)

shutDownOutput():禁用此套接字的输出流

setSoTimeOut():设置指定超时时间(过了这个时间Socket就断开)

其实还有很多,比如获取套接字状态的相关API,获取连接属性相关的API等等。

在上一篇中,我们在服务端还用到了ServerSocket,那就来看看其相关的接口方法,首先还是看看构造方法

ServerSocket()

创建未绑定的服务器套接字。

ServerSocket(int port)

创建绑定到指定端口的服务器套接字。

ServerSocket(int port, int backlog)

创建服务器套接字并将其绑定到指定的本地端口号,并指定了积压。

ServerSocket(int port, int backlog, InetAddress bindAddr)

创建一个具有指定端口的服务器,侦听backlog和本地IP地址绑定。

关于其构造,API也解释的很清楚了,我们上一次使用的是第二个。其接口估计用的最多的要说下面两个了:

accept():监听客户端的连接,并返回Socket对象,阻塞式方法

close():关闭连接

其实,Socket通讯客户端与服务端之间的信息交流主要通过Socket中输出流和输入流来实现。虽然下面这个图在很多地方有,但是我觉得十分有必要在这里放一下:

上面这张图对于下面的这个小示例很有帮助,从图中可以看出,在服务端通过accept()监听客户端连接并获取客户端Socket,之后从Socket中取出对应于客户端输出流的输入流读取数据,也可以像Socket中输出流中写入对应于客户端输入流的数据发送给客户端。可见,这是两条通道,所以,如果我们想实现长连接,在客户端和服务端必然要开启两个线程来交互数据。现在结合这个思想,来想一下我们的示例Demo应该怎么写?可以说非常简单:

  • 服务端构建ServerSocket监听客户端连接
  • 客户端构建Socket进行连接服务器
  • 服务器开启线程不停的监听Socket中InputStream数据(客户端发来的数据),同时开启线程用于向客户端发送消息
  • 客户端开启线程不停的监听Socket中InputStream数据(服务端发来的数据),同时开启线程用于向客户端发送消息
  • 接受到指定指令,客户端断开连接,服务端终止监听。

下面我们就按照这个思路来实现我们的代码,首先还是服务端代码:

package hfut.edu;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerTestCommunicate {

	private static final int PORT = 1234;

	public static void main(String[] args) {
		try {
			ServerSocket server = new ServerSocket(PORT);
			new ReceiveMsgThread(server).start();// 开启消息接收

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

// 处理接收消息的线程
class ReceiveMsgThread extends Thread {

	ServerSocket server;

	public ReceiveMsgThread(ServerSocket server) {
		this.server = server;

	}

	@Override
	public void run() {
		try {

			System.out.println("当前是服务器127.0.0.1窗口:");
			Socket response = server.accept();

			System.out.println("已有客户端连接:" + response.getInetAddress().getHostAddress());
			new SendMsgThread(response).start();// 连接上,开启发送线程
			BufferedReader reader = new BufferedReader(new InputStreamReader(response.getInputStream()));

			String str;
			while ((str = reader.readLine()) != null) {
				// str=URLDecoder.decode(str, "UTF-8");//解码
				System.out.println("msg from client:" + str);
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

// 处理发送消息线程
class SendMsgThread extends Thread {

	Socket response;

	public SendMsgThread(Socket response) {
		this.response = response;
	}

	@Override
	public void run() {
		try {
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(response.getOutputStream()));
			BufferedReader keyboardReader = new BufferedReader(new InputStreamReader(System.in));

			// InputStreamReader in=new InputStreamReader(System.in, "UTF-8");

			String tempStr = "";
			while ((tempStr = keyboardReader.readLine()) != null) {

				// tempStr=URLEncoder.encode(tempStr, "UTF-8");//加码
				writer.write(tempStr + "\n");
				writer.flush();

			}
			System.out.println("结束会话!");
			//response.close();

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}

客户端代码如下:

package hfut.edu;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;

public class ClientTestCommunicate extends Thread {
	public static void main(String[] args) {
		try {
			Socket client = new Socket("127.0.0.1", 1234);
			// client.setSoTimeout(20000);//设置socket连接时间

			System.out.println("当前是客户端窗口:");
			new ReceiveMsgThread(client).start();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

// 接收消息线程
class ReceiveMsgThread extends Thread {

	Socket client;

	public ReceiveMsgThread(Socket client) {
		this.client = client;
	}

	@Override
	public void run() {
		new SendMsgThread(client).start();
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
			String str;
			while (!client.isClosed()&&(str = reader.readLine()) != null) {
				System.out.println("msg from server:" + str);
			}

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

// 发送消息线程
class SendMsgThread extends Thread {

	Socket response;

	public SendMsgThread(Socket response) {
		this.response = response;
	}

	@Override
	public void run() {
		try {
			BufferedReader keyboardReader = new BufferedReader(new InputStreamReader(System.in));
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(response.getOutputStream()));
			String tempStr = "";
			while (!tempStr.contains("bye")) {
				tempStr = keyboardReader.readLine();
				// tempStr=URLEncoder.encode(tempStr, "UTF-8");//加码
				writer.write(tempStr + "\n");
				writer.flush();
			}
			System.out.println("结束会话!");
			keyboardReader.close();
			writer.close();
			response.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

代码运行之后就可以实现长连接并进行通讯了。这里面还有一些不过未修复,后面会修改后上新代码。

注:欢迎扫码关注

 

 

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值