java基于UDP實(shí)現(xiàn)圖片群發(fā)功能
UDP協(xié)議(用戶(hù)數(shù)據(jù)報(bào)協(xié)議)是一種不可靠的網(wǎng)絡(luò)協(xié)議,它在通信實(shí)例的兩端各建立一個(gè)Socket,但是這兩個(gè)Socket之間并沒(méi)有虛擬鏈路,這兩個(gè)Socket只是發(fā)送,接收數(shù)據(jù)報(bào)的對(duì)象。
UDP的優(yōu)缺點(diǎn):
1. 因?yàn)閁DP協(xié)議是面向非連接的協(xié)議,沒(méi)有建立連接的過(guò)程,因此它的通信效率很高。很適合一些即時(shí)性很強(qiáng)的應(yīng)用場(chǎng)景。
2.因?yàn)樵谡酵ㄐ徘安槐嘏c對(duì)方先連接,不管對(duì)方狀態(tài)就直接發(fā)送,至于對(duì)方是否可以收到這些數(shù)據(jù)內(nèi)容,UDP無(wú)法控制,所以說(shuō)UDP是一種不可靠的協(xié)議。
3.傳輸大小限制在64KB以下,這個(gè)尤其要注意,在做這個(gè)實(shí)例的時(shí)候,因?yàn)闆](méi)有考慮到這個(gè),直接傳了一張大圖,結(jié)果找了半天的原因。
Java使用DatagramSocket代表UDP協(xié)議的Socket,它唯一的作用是接收和發(fā)送數(shù)據(jù)報(bào),至于數(shù)據(jù)究竟發(fā)給誰(shuí),DatagramSocket并不清楚;具體發(fā)送的目的地是由DatagramPacket自身決定。當(dāng)Client/Server程序使用UDP協(xié)議時(shí),實(shí)際上并沒(méi)有嚴(yán)格的服務(wù)器和客戶(hù)端的區(qū)分。通常固定IP地址,固定端口的DatagramSocket對(duì)象所在程序被稱(chēng)為服務(wù)器,因?yàn)橛泄潭ǖ腎P,端口地址,其他客戶(hù)端的數(shù)據(jù)報(bào)可以直接發(fā)送到服務(wù)器上。
接收數(shù)據(jù)的DatagramPacket在實(shí)例化時(shí)無(wú)需指定端口和IP地址,給出數(shù)據(jù)數(shù)據(jù)的字節(jié)數(shù)組以及長(zhǎng)度即可。然后調(diào)用DatagramSocket的receive()方法等待數(shù)據(jù)報(bào)的到來(lái),該方法阻塞線(xiàn)程直到受到一個(gè)數(shù)據(jù)報(bào)為止。
發(fā)送數(shù)據(jù)的DatagramPacket不同的是,需要給出完整的目的地,包括IP地址和端口,這樣數(shù)據(jù)報(bào)才能知道將數(shù)據(jù)發(fā)給誰(shuí)。當(dāng)服務(wù)器接收到一個(gè)DatagramPacket對(duì)象后,如果想向該數(shù)據(jù)報(bào)的發(fā)送者反饋一些消息,但由于UDP協(xié)議是面向非連接的,所以不知道數(shù)據(jù)報(bào)是誰(shuí)發(fā)送過(guò)來(lái)的,但程序可以調(diào)用DatagramPacket的getAddress()(返回一個(gè)InetAddress對(duì)象,發(fā)報(bào)的IP地址),getPort()(返回發(fā)報(bào)的端口)和getSocketAddress()(返回一個(gè)SocketAddress對(duì)象,該對(duì)象可以同時(shí)代表IP地址和端口)。
實(shí)現(xiàn)思路:每個(gè)客戶(hù)端啟動(dòng)時(shí)都會(huì)向服務(wù)端發(fā)送一個(gè)字符串,該字符串代表該客戶(hù)端已經(jīng)上線(xiàn),并在服務(wù)端將每個(gè)客戶(hù)端的發(fā)報(bào)地址(即SocketAddress對(duì)象)保存在一個(gè)Set集合中。當(dāng)點(diǎn)擊任意一個(gè)上線(xiàn)的客戶(hù)端的發(fā)送圖片按鈕,該圖片數(shù)據(jù)就會(huì)被發(fā)送到服務(wù)端上,服務(wù)端遍歷SocketAddress集合,并將圖片數(shù)據(jù)轉(zhuǎn)發(fā)到每個(gè)SocketAddress對(duì)應(yīng)的客戶(hù)端上,就實(shí)現(xiàn)了簡(jiǎn)單的圖片群發(fā)。具體代碼如下:
客戶(hù)端發(fā)送數(shù)據(jù)報(bào)的工具類(lèi):
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實(shí)例 private DatagramSocket singleSocket = null; //定義接收網(wǎng)絡(luò)數(shù)據(jù)的字符數(shù)組 byte[] inBuff = new byte[DATA_LEN]; private Handler handler; //構(gòu)造器,初始化資源 public DatagramUtil(Handler handler) throws Exception { this.handler = handler; //創(chuàng)建用于私聊的DatagramSocket對(duì)象 singleSocket = new DatagramSocket(); new ReadSingle().start(); } //定義單獨(dú)用戶(hù)發(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中讀取數(shù)據(jù)的線(xiàn)程 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中的數(shù)據(jù) try { //讀取Socket中的數(shù)據(jù) singleSocket.receive(singlePacket); //處理得到的消息 Message msg = new Message(); msg.what = 0x123; msg.obj = singleBuff; handler.sendMessage(msg); } catch (IOException e) { e.printStackTrace(); if (singleSocket != null) { //關(guān)閉該Socket對(duì)象 singleSocket.close(); } } } } } }
收到服務(wù)端發(fā)來(lái)的圖片數(shù)據(jù)時(shí),使用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(); } }
服務(wù)端代碼(運(yùn)行該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(); } }
這樣實(shí)現(xiàn)了簡(jiǎn)單的圖片群發(fā)的效果。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- java實(shí)現(xiàn)基于UDP協(xié)議網(wǎng)絡(luò)Socket編程(C/S通信)
- java網(wǎng)絡(luò)之基于UDP的聊天程序示例解析
- Java模擬UDP通信示例代碼
- Java實(shí)現(xiàn)UDP通信過(guò)程實(shí)例分析【服務(wù)器端與客戶(hù)端】
- java UDP通信客戶(hù)端與服務(wù)器端實(shí)例分析
- Java基于ServletContextListener實(shí)現(xiàn)UDP監(jiān)聽(tīng)
- Java編程使用UDP建立群聊系統(tǒng)代碼實(shí)例
- 基于Java中UDP的廣播形式(實(shí)例講解)
- Java NIO實(shí)例UDP發(fā)送接收數(shù)據(jù)代碼分享
- 淺談java的TCP和UDP編程(附實(shí)例講解)
- java 多線(xiàn)程實(shí)現(xiàn)在線(xiàn)咨詢(xún)(udp)
相關(guān)文章
Java虛擬機(jī)類(lèi)加載器之雙親委派機(jī)制模型案例
這篇文章主要介紹了Java虛擬機(jī)類(lèi)加載器之雙親委派機(jī)制模型案例,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08springboot+zookeeper實(shí)現(xiàn)分布式鎖的示例代碼
本文主要介紹了springboot+zookeeper實(shí)現(xiàn)分布式鎖的示例代碼,文中根據(jù)實(shí)例編碼詳細(xì)介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Mybatis實(shí)體類(lèi)屬性與數(shù)據(jù)庫(kù)不一致解決方案
這篇文章主要介紹了Mybatis實(shí)體類(lèi)屬性與數(shù)據(jù)庫(kù)不一致解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10關(guān)于Java跨域Json字符轉(zhuǎn)類(lèi)對(duì)象的方法示例
這篇文章主要給大家介紹了關(guān)于Java跨域Json字符轉(zhuǎn)類(lèi)對(duì)象的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11Java EasyExcel讀寫(xiě)excel如何解決poi讀取大文件內(nèi)存溢出問(wèn)題
這篇文章主要介紹了Java EasyExcel讀寫(xiě)excel如何解決poi讀取大文件內(nèi)存溢出問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06java實(shí)現(xiàn)的n*n矩陣求值及求逆矩陣算法示例
這篇文章主要介紹了java實(shí)現(xiàn)的n*n矩陣求值及求逆矩陣算法,結(jié)合具體實(shí)例形式分析了java基于數(shù)組的矩陣定義、遍歷、運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下2017-09-09MongoDB中ObjectId的誤區(qū)及引起的一系列問(wèn)題
這篇文章主要介紹了MongoDB中ObjectId的誤區(qū)及引起的一系列問(wèn)題,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-12-12