实际工作中,基于Socket的网络通信应用非常广泛,本人写了个源码,小试牛刀,实现的功能如下:
1,模拟一个服务端和多个客户端进行交互;
2,在客户端可将一个java对象进行序列化,并发送给服务端;
3,服务端接收到客户端发送过来的序列化对象后,进行反系列化。
设计思路是:
使用套接字实现基于TCP协议的服务器和客户机程序
依据TCP协议,在B/S架构的通讯过程中,客户端和服务器的Socket动作如下:
客户端:
1.用服务器的IP地址和端口号实例化Socket对象。
2.调用connect方法,连接到服务器上。
3.将实体对象序列化后发送到服务器的IO流填充到IO对象里,如ObjectOutputStream。
4.利用Socket提供的getOutputStream方法,通过IO流对象,向服务器发送数据流。
5. 通讯完成后,关闭打开的IO对象和Socket。
服务器端:
1、在服务器,用一个端口来实例化一个 ServerSocket对象。此时,服务器就可以在这个端口时刻监听从客户端发来的连接请求。
2、调用ServerSocket的accept方法,开始监听连接从端口上发来的连接请求。
3、利用accept方法返回的客户端的Socket对象,进行读写IO的操作。
4、利用IO流对象中的ObjectInputStream封装Socket对象提供的getInputStream方法,反序列化实体对象。
5、通讯完成后,关闭打开的流和Socket对象。
相关性能优化方案:
在服务器端利用多线程实现客户端和服务器的通信,当Server端接受到一个Client连接请求之后,就把处理流程放到一个独立的子线程里去运行,然后等待下一个Client连接请求,这样就不会阻塞Server端接收请求了。每个独立运行的程序在使用完Socket对象之后将其关闭,并结束该条子线程。(子线程用内部类封装起来)
源码如下:
1.客户端程序
package test.socket; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.Socket; import java.util.logging.Level; import java.util.logging.Logger; /** * 客户端,对象系列化 后传输到服务器端 * @author Administrator * */ public class SocketClient { private final static Logger logger = Logger.getLogger(SocketClient.class.getName()); public static void main(String[] args) throws Exception { for (int i = 0; i < 5; i++) { Socket socket = null; ObjectOutputStream os = null; try { socket = new Socket("localhost", 30000); //此处绑定的ip地址应该根据具体服务器的地址来填写。 os = new ObjectOutputStream(socket.getOutputStream()); User user = new User("user_" + i, "password_" + i); os.writeObject(user); os.flush(); } catch (IOException ex) { logger.log(Level.SEVERE, null, ex); } finally { try { os.close(); } catch (Exception ex) { } try { socket.close(); } catch (Exception ex) { } } } } }
2.服务器端程序
package test.socket;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Logger;
/**
* 服务器期端接收 客户端传过来的序列化对象 并反序列化
* @author Administrator
*
*/
public class SocketService {
private final static Logger logger = Logger.getLogger(SocketService.class.getName());
private ServerSocket server;
/**
* 监听服务器端的 30000端口
* @throws IOException
*/
public void start() throws IOException
{
// String hostIP = InetAddress.getLocalHost().getHostAddress() ; //服务器上的地址
// logger.info("服务器端hostIP---------------->" + hostIP);
InetAddress iAddress = InetAddress.getByName("localhost");
server = new ServerSocket(30000, 50, iAddress); // 监听30000端口,等待请求通过网络传入
}
/**
* 与客户端建立连接(多线程并发控制)
*/
private void accept()
{
//启动线程
new Thread()
{
@Override
public void run()
{
while (true) //监听端口需要一个死循环,来实现24小时不间隔运行
{
try
{
logger.info("监听客户端accept.....");
Socket socket = server.accept(); //侦听并接受客户端连接,
socket.setOOBInline(true); //
socket.setTcpNoDelay(true);
logger.info("new client......" );
processRequest(socket); //一个客户端连接用一个线程来处理,处理完该线程就结束。
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}.start();
}
/**
* 实现客户端套接字
* @param socket
*/
private void processRequest(Socket socket)
{
new RequestProcessor(socket).start(); //用内部类处理客户端的业务请求(好处?可以让jvm回收相关的子线程,并很好的隐藏了内部逻辑)
}
/**
* 实现具体业务逻辑的内部类
* @author Administrator *
*/
private class RequestProcessor extends Thread
{
Socket socket;
//用内部类构造方法初始化线程
public RequestProcessor(Socket socket)
{
super();
this.socket = socket ;
}
@Override
public void run()
{
try
{
ObjectInputStream is = new ObjectInputStream(new BufferedInputStream(socket.getInputStream()));
Object obj = is.readObject();
if (obj != null) {
User user = (User)obj;
logger.info("反序列化user: " + user.getName() + "/" + user.getPassword());
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
/**
* 服务器端hostIP的 接口服务
*/
public static void startServer()
{
try
{
SocketService socketService = new SocketService(); //因为静态方法不能直接调用非静态方法
socketService.start();
socketService.accept();
}
catch(IOException e)
{
e.printStackTrace();
}
}
/**
* 启动服务器端服务
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException
{
startServer();
}
}
3.待序列化 和 反序列化的 实体类
package test.socket; /** * 待序列化传输的对象 * @author Administrator * */ public class User implements java.io.Serializable { private static final long serialVersionUID = 1L; private String name; private String password; public User() { } public User(String name, String password) { this.name = name; this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
代码运行解释:
已测试通过,先运行SocketService类,再运行SocketClient类,就可以在SocketService的控制台端看见分别接收处理每个Client的请求了(注:在SocketClient的控制台是看不到的)