Java通過 Socket 實現(xiàn) TCP服務端
1 Java Socket簡介
所謂socket 通常也稱作”套接字“,用于描述IP地址和端口,是一個通信鏈的句柄。應用程序通常通過”套接字”向網(wǎng)絡發(fā)出請求或者應答網(wǎng)絡請求。Socket和ServerSocket類庫位于Java.NET包中。ServerSocket用于服務器端,Socket是建立網(wǎng)絡連接時使用的。在連接成功時,應用程序兩端都會產(chǎn)生一個Socket實例,操作這個實例,完成所需的會話。對于一個網(wǎng)絡連接來說,套接字是平等的,并沒有差別,不因為在服務器端或在客戶端而產(chǎn)生不同級別。
2 TCPServer代碼實例
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* TCP服務器端,單例模式
* @author xiang
*
*/
public class TCPServer implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(TCPServer.class);
//成員變量/
private static TCPServer serverInstance;
private static Map<String, SocketThread> socketMaps = new HashMap<String,SocketThread>(); //每個客戶端連接時都會新建一個SocketThread與之對應 private static ServerSocket serverSocket; //服務器套接字
private static int serPort = 9999; //服務器端口號
private static boolean flag; //服務器狀態(tài)標志
private static final int BUFFER_SIZE = 512; //數(shù)據(jù)接收字符數(shù)組大小
//構造函數(shù)/
private TCPServer() {
}
/**
* 獲取實例
* @return TCPServer實例serverInstance
*/
public static TCPServer getServerInstance(){
if(serverInstance==null)
serverInstance = new TCPServer();
return serverInstance;
}
/**
* 開啟服務器
* @throws IOException
*/
public void openTCPServer() throws IOException{ if(serverSocket==null || serverSocket.isClosed()){
serverSocket = new ServerSocket(serPort);
flag = true;
}
}
/**
* 關閉服務器
* @throws IOException
*/
public void closeTCPServer() throws IOException{
flag = false; if(serverSocket!=null)
serverSocket.close();
/*for (Map.Entry<String, SocketThread> entry : socketMaps.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
} */
for (SocketThread value : socketMaps.values())
value.closeConnect();
socketMaps.clear();
}
/**
* 服務器向客戶端發(fā)送數(shù)據(jù)
* @param bytes[]:待發(fā)送的字符數(shù)組
* @param key 客戶端的key,為空或""時表示數(shù)據(jù)群發(fā)
* @throws IOException
*/
public void sendMessage(String key,byte[] msgBytes){
if(key==null||key.equals("")){
for (SocketThread value : socketMaps.values())
value.sendMassage(msgBytes);
}else{
SocketThread thread = socketMaps.get(key);
if(thread!=null)
thread.sendMassage(msgBytes);
}
}
/**
* 服務器向客戶端發(fā)送數(shù)據(jù)
* @param key 客戶端的key,為空或""時表示數(shù)據(jù)群發(fā)
* @param msgStr:待發(fā)送的字符串
* @throws IOException
*/
public void sendMessage(String key,String msgStr){ byte[] sendByte = msgStr.getBytes();
if(key==null||key.equals("")){
for (SocketThread value : socketMaps.values())
value.sendMassage(sendByte);
}else{
SocketThread thread = socketMaps.get(key);
if(thread!=null)
thread.sendMassage(sendByte);
}
}
@Override
public void run() {
logger.info("服務器線程已經(jīng)啟動"); while(true){
try {
while(flag){
logger.info("服務器線程在監(jiān)聽狀態(tài)中");
Socket socket = serverSocket.accept();
String key = socket.getRemoteSocketAddress().toString();
SocketThread thread = new SocketThread(socket,key);
thread.start();
socketMaps.put(key, thread);
logger.info("有客戶端連接:"+key);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 處理連接后的數(shù)據(jù)接收請求內(nèi)部類
* @author xiang
*
*/
private class SocketThread extends Thread{
private Socket socket;
private String key;
private OutputStream out;
private InputStream in;
//構造函數(shù)
public SocketThread(Socket socket,String key) {
this.socket = socket;
this.key = key;
}
/**
* 發(fā)送數(shù)據(jù)
* @param bytes
* @throws IOException
*/
public void sendMassage(byte[] bytes){
try {
if(out==null)
out = socket.getOutputStream();
out.write(bytes);
} catch (Exception e) {
e.printStackTrace();
try {
closeConnect();
} catch (IOException e1) {
e1.printStackTrace();
}
socketMaps.remove(key);
}
}
/**
* 關閉連接,釋放資源
* @throws IOException
*/
public void closeConnect() throws IOException{
if(out!=null) out.close();
if(in!=null) in.close();
if(socket!=null && socket.isConnected()) socket.close();
}
@Override
public void run() {
byte[] receivBuf = new byte[BUFFER_SIZE];
int recvMsgSize;
try {
in = socket.getInputStream();
out = socket.getOutputStream();
while ((recvMsgSize = in.read(receivBuf)) != -1) {
String receivedData = new String(receivBuf, 0, recvMsgSize);
System.out.println("Reverve form[port" + socket.getPort() + "]:" + receivedData);
System.out.println("Now the size of socketMaps is" + socketMaps.size());
/**************************************************************
*
* 接收數(shù)據(jù)后的處理過程
*
**************************************************************/
}
// response to client
byte[] sendByte = "The Server has received".getBytes();
// out.write(sendByte, 0, sendByte.length);
out.write(sendByte);
System.out.println("To Cliect[port:" + socket.getPort() + "] 回復客戶端的消息發(fā)送成功");
closeConnect();
socketMaps.remove(key);
} catch (Exception e) {
e.printStackTrace();
try {
closeConnect();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
//////////////
public int getport(){
return socket.getPort();
}
}
//. end SocketThread
}
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關文章
關于springboot中nacos動態(tài)路由的配置
這篇文章主要介紹了springboot中nacos動態(tài)路由的配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringBoot日志進階實戰(zhàn)之Logback配置經(jīng)驗和方法
本文給大家介紹在SpringBoot中使用Logback配置日志的經(jīng)驗和方法,并提供了詳細的代碼示例和解釋,包括:滾動文件、異步日志記錄、動態(tài)指定屬性、日志級別、配置文件等常用功能,覆蓋日常Logback配置開發(fā)90%的知識點,感興趣的朋友跟隨小編一起看看吧2023-06-06
hutool實戰(zhàn):IoUtil 流操作工具類(將內(nèi)容寫到流中)
這篇文章主要介紹了Go語言的io.ioutil標準庫使用,是Golang入門學習中的基礎知識,需要的朋友可以參考下,如果能給你帶來幫助,請多多關注腳本之家的其他內(nèi)容2021-06-06
使用Backoff策略提高HttpClient連接管理的效率
這篇文章主要為大家介紹了Backoff策略提高HttpClient連接管理的效率使用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
ConcurrentHashMap是如何實現(xiàn)線程安全的你知道嗎
這篇文章主要介紹了ConcurrentHashMap是如何實現(xiàn)線程安全的你知道嗎,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10

