java基于UDP實現圖片群發(fā)功能
UDP協議(用戶數據報協議)是一種不可靠的網絡協議,它在通信實例的兩端各建立一個Socket,但是這兩個Socket之間并沒有虛擬鏈路,這兩個Socket只是發(fā)送,接收數據報的對象。
UDP的優(yōu)缺點:
1. 因為UDP協議是面向非連接的協議,沒有建立連接的過程,因此它的通信效率很高。很適合一些即時性很強的應用場景。
2.因為在正式通信前不必與對方先連接,不管對方狀態(tài)就直接發(fā)送,至于對方是否可以收到這些數據內容,UDP無法控制,所以說UDP是一種不可靠的協議。
3.傳輸大小限制在64KB以下,這個尤其要注意,在做這個實例的時候,因為沒有考慮到這個,直接傳了一張大圖,結果找了半天的原因。
Java使用DatagramSocket代表UDP協議的Socket,它唯一的作用是接收和發(fā)送數據報,至于數據究竟發(fā)給誰,DatagramSocket并不清楚;具體發(fā)送的目的地是由DatagramPacket自身決定。當Client/Server程序使用UDP協議時,實際上并沒有嚴格的服務器和客戶端的區(qū)分。通常固定IP地址,固定端口的DatagramSocket對象所在程序被稱為服務器,因為有固定的IP,端口地址,其他客戶端的數據報可以直接發(fā)送到服務器上。
接收數據的DatagramPacket在實例化時無需指定端口和IP地址,給出數據數據的字節(jié)數組以及長度即可。然后調用DatagramSocket的receive()方法等待數據報的到來,該方法阻塞線程直到受到一個數據報為止。
發(fā)送數據的DatagramPacket不同的是,需要給出完整的目的地,包括IP地址和端口,這樣數據報才能知道將數據發(fā)給誰。當服務器接收到一個DatagramPacket對象后,如果想向該數據報的發(fā)送者反饋一些消息,但由于UDP協議是面向非連接的,所以不知道數據報是誰發(fā)送過來的,但程序可以調用DatagramPacket的getAddress()(返回一個InetAddress對象,發(fā)報的IP地址),getPort()(返回發(fā)報的端口)和getSocketAddress()(返回一個SocketAddress對象,該對象可以同時代表IP地址和端口)。
實現思路:每個客戶端啟動時都會向服務端發(fā)送一個字符串,該字符串代表該客戶端已經上線,并在服務端將每個客戶端的發(fā)報地址(即SocketAddress對象)保存在一個Set集合中。當點擊任意一個上線的客戶端的發(fā)送圖片按鈕,該圖片數據就會被發(fā)送到服務端上,服務端遍歷SocketAddress集合,并將圖片數據轉發(fā)到每個SocketAddress對應的客戶端上,就實現了簡單的圖片群發(fā)。具體代碼如下:
客戶端發(fā)送數據報的工具類:
public class DatagramUtil
{
public static final int BOADCAST_PORT = 8888;
public static final String DEST_IP = "192.168.1.101";
private static final int DATA_LEN = 50000;
//定義本程序私聊的Socket實例
private DatagramSocket singleSocket = null;
//定義接收網絡數據的字符數組
byte[] inBuff = new byte[DATA_LEN];
private Handler handler;
//構造器,初始化資源
public DatagramUtil(Handler handler) throws Exception
{
this.handler = handler;
//創(chuàng)建用于私聊的DatagramSocket對象
singleSocket = new DatagramSocket();
new ReadSingle().start();
}
//定義單獨用戶發(fā)送消息的方法
public void sendSingle(byte[] msg)
{
try
{
DatagramPacket packet = new DatagramPacket(new byte[0] , 0 , InetAddress.getByName(DEST_IP) , BOADCAST_PORT);
packet.setData(msg);
singleSocket.send(packet);
}
catch (IOException e)
{
e.printStackTrace();
}
}
//不斷地從DatagramSocket中讀取數據的線程
class ReadSingle extends Thread
{
byte[] singleBuff = new byte[DATA_LEN];
private DatagramPacket singlePacket = new DatagramPacket(singleBuff , singleBuff.length);
@Override
public void run()
{
while (true)
{
// 讀取Socket中的數據
try
{
//讀取Socket中的數據
singleSocket.receive(singlePacket);
//處理得到的消息
Message msg = new Message();
msg.what = 0x123;
msg.obj = singleBuff;
handler.sendMessage(msg);
}
catch (IOException e)
{
e.printStackTrace();
if (singleSocket != null)
{
//關閉該Socket對象
singleSocket.close();
}
}
}
}
}
}
收到服務端發(fā)來的圖片數據時,使用Handler更新UI。
public class MainActivity extends Activity
{
private Button button;
private ImageView img;
private DatagramUtil datagramUtil;
Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
if (msg.what == 0x123)
{
byte[] result = (byte[]) msg.obj;
img.setImageBitmap(BitmapFactory.decodeByteArray(result , 0 , result.length));
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
button = (Button) findViewById(R.id.send_img_all);
img = (ImageView) findViewById(R.id.receiver_img);
try
{
datagramUtil = new DatagramUtil(handler);
sendData(stringYoByte());
}
catch (Exception e)
{
e.printStackTrace();
}
button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
sendData(bitMapToByte());
}
});
}
private void sendData(final byte[] msg)
{
new Thread()
{
@Override
public void run()
{
datagramUtil.sendSingle(msg);
}
}.start();
}
public byte[] bitMapToByte()
{
Bitmap bitmap = BitmapFactory.decodeResource(getResources() , R.drawable.wenqing);
ByteArrayOutputStream byteArray = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG , 100 , byteArray);
bitmap.recycle();
return byteArray.toByteArray();
}
public byte[] stringYoByte()
{
String loginStr = "hello";
return loginStr.getBytes();
}
}
服務端代碼(運行該Java程序即可):
public class UDPServer
{
public static final int PORT = 8888;
private static final int DATA_LEN = 50000;
byte[] inBuff = new byte[DATA_LEN];
private DatagramPacket inPacket = new DatagramPacket(inBuff , inBuff.length);
private DatagramPacket outPacket;
private DatagramSocket serverSocket;
private Set<SocketAddress> socketAddressList = Collections.synchronizedSet(new HashSet<SocketAddress>());
public void init() throws IOException
{
serverSocket = new DatagramSocket(PORT);
while (true)
{
serverSocket.receive(inPacket);
String result = new String(inBuff , 0 , inBuff.length);
if (result.trim().equals("hello"))
{
socketAddressList.add(inPacket.getSocketAddress());
}
else
{
for (Iterator<SocketAddress> iterator = socketAddressList.iterator(); iterator.hasNext() ; )
{
SocketAddress socketAddress = iterator.next();
outPacket = new DatagramPacket(inBuff , inBuff.length , socketAddress);
serverSocket.send(outPacket);
}
}
}
}
public static void main(String[] args) throws IOException
{
new UDPServer().init();
}
}
這樣實現了簡單的圖片群發(fā)的效果。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
詳解Java中synchronized關鍵字的死鎖和內存占用問題
Java的synchronized關鍵字用來進行線程同步操作,然而這在使用中經常會遇到一些問題,這里我們就來詳解Java中synchronized關鍵字的死鎖和內存占用問題:2016-06-06
springboot如何實現異步響應請求(前端請求超時的問題解決)
這篇文章主要給大家介紹了關于springboot如何實現異步響應請求(前端請求超時的問題解決)的相關資料,文中通過實例代碼介紹的非常詳細,對大家學習或者使用springboot具有一定的參考學習價值,需要的朋友可以參考下2023-01-01
httpclient ConnectionHolder連接池連接保持源碼解析
這篇文章主要為大家介紹了httpclient ConnectionHolder連接池連接保持源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11
Java EasyExcel讀寫excel如何解決poi讀取大文件內存溢出問題
這篇文章主要介紹了Java EasyExcel讀寫excel如何解決poi讀取大文件內存溢出問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06

