实现一个聊天室可发送消息语音图片视频表情包(任意文件)
文章目录
- 如何跑通
- 代码仓库地址
- 客户端
- 登录
- 发送消息
- 接受消息
- 发送文件
- 接受文件
- 服务端
- 接受消息并发送给各个客户端
- 接受文件并发送给各个客户端
如何跑通
- 将手机和电脑都连自己的热点
- 先运行服务器得到可监听的地址
- 更新客户端安卓消息线程和文件线程的socker目标地址为可监听地址
- 然后数据线连接手机运行,此时手机便多了个app,然后可以不需要数据线单独运行了
代码仓库地址
https://github.com/FULLK/llkchatroom/
客户端
登录
输入用户名。获取输入的用户名和通信IP
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);input_name = (TextView) findViewById(R.id.input_name);Button confirm = (Button) findViewById(R.id.confirm);confirm.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Name.name = input_name.getText().toString();//得到输入的字符串Name.IP = getLocalIpAddress();Log.e("Register", Name.IP + Name.name);if (!Name.name.equals("")) {//输入内容不为空那么点击就跳转到chatromm界面Intent intent = new Intent(MainActivity.this, Chatroom.class);startActivity(intent);}}});}public static String getLocalIpAddress() {try {for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {NetworkInterface intf = en.nextElement();for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {InetAddress inetAddress = enumIpAddr.nextElement();if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress()) {return inetAddress.getHostAddress().toString();}}}} catch (SocketException ex) {Log.e("WifiPreference IpAddre", ex.toString());}return null;}
首先是输入用户名存到Name结构体中
public class Name {public static String name ;public static String IP;
}
这段Java代码遍历了本机的所有网络接口(NetworkInterface),然后对于每个网络接口,进一步遍历其绑定的所有IP地址(InetAddress)。其核心目的是找到并返回一个符合条件的IPv4或IPv6地址,该地址既不是回环地址(loopback address,如127.0.0.1),也不是链路本地地址(link-local address,这类地址仅用于同一链路上的通信,如IPv6的fe80::/10范围内的地址)。具体步骤如下:
-
获取网络接口枚举:首先通过
NetworkInterface.getNetworkInterfaces()方法获取到本机所有网络接口的枚举(Enumeration)对象。网络接口可以理解为计算机上的物理或虚拟网卡。 -
遍历网络接口:使用
hasMoreElements()和nextElement()方法遍历所有的网络接口。对于每个网络接口intf: -
获取IP地址枚举:通过
intf.getInetAddresses()方法获取该网络接口上绑定的所有IP地址的枚举。 -
遍历IP地址:再次使用
hasMoreElements()和nextElement()遍历这些IP地址。对于每个IP地址inetAddress: -
检查地址类型:使用
isLoopbackAddress()方法检查这个IP地址是否是回环地址,使用isLinkLocalAddress()方法检查是否是链路本地地址。这两个条件都不满足,意味着这个IP地址是可外部访问的地址。 -
返回符合条件的IP地址:一旦找到一个既不是回环地址也不是链路本地地址的IP地址,就立即通过
getHostAddress().toString()获取其字符串表示形式并返回。这意味着该方法最终返回的是本机的第一个非回环、非链路本地的IP地址。
发送消息
定义消息类
public class Msg {public static final int TYPE_RECEIVED = 0;//收到的消息public static final int TYPE_SENT = 1;//发出去的消息private String name;private String content;private int type;//content表示消息内容,type表示类型public Msg(String name,String content ,int type){this.name = name;this.content = content;this.type = type;}public String getContent(){return content;}public int getType(){return type;}public String getName() {return name;}
}
点击按钮后发送消息
send.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String content = inputTest.getText().toString();Log.e("get from input", content);if (!"".equals(content)) {try {//将输入框的信息传递给msg,并标记Message handleMsg = new Message();handleMsg.what = 1;handleMsg.obj = inputTest.getText().toString();//将msg传递给发送子线程mClientThread.revHandler.sendMessage(handleMsg);//输入框变空inputTest.setText("");} catch (Exception e) {e.printStackTrace();}}}});
子线程不断循环运行实现发送消息
Looper.prepare();//绑定发送线程的Handler//由chatroom点击事件跳转到这里发送消息revHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == 1) {try {//发送消息String content;content =Name.IP+"#$#" + Name.name+"#$#" + msg.obj.toString() + "\r\n";mOutputStream.write(content.getBytes("utf-8"));} catch (IOException e) {e.printStackTrace();}}}};//Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。Looper.loop();} catch (IOException e) {e.printStackTrace();Log.d("error","");}
接受消息
子线程循环接受服务端的消息
new Thread(){@Overridepublic void run() {super.run();try {String content = null;//一个新线程持续循环的接受从服务器的消息,再发送给chatroomwhile ((content = mBufferedReader.readLine()) != null) {Log.d("get from server",content);//将接受到的数据传递给msg对象,并标记Message handleMsg = new Message();handleMsg.what = 0;handleMsg.obj = content;mHandler.sendMessage(handleMsg);}}catch (IOException e){e.printStackTrace();}}}.start();//启动
然后发送给主线程,主线程根据接受到的消息来更新聊天界面
mHandler = new Handler() {@Overridepublic void handleMessage(Message handleMsg) {if (handleMsg.what == 0) {//接受到消息后的操作String content = handleMsg.obj.toString();Log.d("recive", content);String[] arr = content.split("#\\$#");String ip = arr[0];String name = arr[1];String str = arr[2];Log.d("get ", ip + name + str);Msg msg;if (ip.equals(Name.IP)) {Log.e("recive from server", "it is me ");msg = new Msg(name, str, Msg.TYPE_SENT);} else {msg = new Msg(name, str, Msg.TYPE_RECEIVED);}msgList.add(msg);Log.e("TAG", "msg " + msgList.size());adapter.notifyItemInserted(msgList.size() - 1);//当有新消息时,刷新RecyclView中的显示msgRecyclerView.scrollToPosition(msgList.size() - 1);//将RecyclerView定位到最后一行inputTest.setText("");//清空输入框*/}}};
发送文件
首先选择文件
file.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 创建意图用于选择文件//Intent intent = new Intent(Intent.ACTION_GET_CONTENT);//intent.setType("*/*");/*if (intent.resolveActivity(getPackageManager()) != null) {startActivityForResult(Intent.createChooser(intent, "选择文件"), PICK_FILE_REQUEST_CODE);} else {Toast.makeText(context, "无法找到文件选择器", Toast.LENGTH_SHORT).show();}*/Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.addCategory(Intent.CATEGORY_OPENABLE);intent.setType("*/*");startActivityForResult(intent, PICK_FILE_REQUEST_CODE);}});
然后对选择到的文件的返回结果进行处理
protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == PICK_FILE_REQUEST_CODE && resultCode == RESULT_OK) {try {if (data != null) {Uri uri = data.getData();Log.e("uri", ":" + uri);String filePath = "";// 根据Android版本的不同,获取文件路径的方式也有所不同// 在API 19(KitKat)及以上版本,需要通过ContentResolver查询文件的真实路径if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(this, uri)) {// 处理DocumentsProvider的情况ContentResolver resolver = getContentResolver();InputStream inputStream = null;inputStream = resolver.openInputStream(uri);String filename = getFileNameFromUri(this, uri);Log.e("filename: ", ":" + filename);Log.e("inputstream: ", ":" + inputStream);Log.e("uri: ", ":" + uri);if (uri != null) {handleSelectedFilePath(filename,uri);}}}}catch (FileNotFoundException e) {e.printStackTrace();}}}
其中处理返回结果调用下列函数得到了文件名,并且将选择的文件写到了一个新建的可知道文件路径的文件(因为不能根据返回结果得到文件路径)
public String getFileNameFromUri (Context context, Uri uri){String fileName = null;Cursor cursor = null;try {cursor = context.getContentResolver().query(uri, null, null, null, null);if (cursor != null && cursor.moveToFirst()) {int columnIndex = cursor.getColumnIndex(MediaStore.Files.FileColumns.DISPLAY_NAME);fileName = cursor.getString(columnIndex);}} finally {if (cursor != null) {cursor.close();}}return fileName;}private void handleSelectedFilePath (String filename, Uri file){// 在这里处理获取到的文件路径Log.e("TAG", "Selected file name" + filename);// 可以进一步上传文件、读取文件内容等操作try {// 假设你已经有了一个Uri对象Uri sourceUri = file;// 获取源文件的输入流InputStream inputStream = getContentResolver().openInputStream(sourceUri);// 定义目标文件路径,这里以应用程序的cache目录为例String destFilePath = getCacheDir().getPath() + "/"+filename;File destFile = new File(destFilePath);// 创建并获取目标文件的输出流FileOutputStream outputStream = new FileOutputStream(destFile);// 将源文件内容复制到新文件byte[] buffer = new byte[1024];int read;while ((read = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, read);}// 关闭输入输出流outputStream.flush();outputStream.close();inputStream.close();// 现在你可以得到新建文件的文件地址String newFileAddress = destFile.getAbsolutePath();Log.e("getAbsolutePath", ": "+newFileAddress );//将输入框的信息传递给msg,并标记Message handleMsg = new Message();handleMsg.what = 1;handleMsg.obj =newFileAddress ;//"/data/data/llk/files/"//将msg传递给发送子线程fClientThread.revfHandler.sendMessage(handleMsg);//输入框变空inputTest.setText("");} catch (Exception e) {e.printStackTrace();}}}
最后handleSelectedFilePath函数将包含新建文件地址发送到子线程,子线程将文件名字和文件长度和文件字节发送到服务端
Looper.prepare();//绑定发送线程的Handler//由chatroom点击事件跳转到这里发送消息revfHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == 1) {try {mOutputStream = fSocket.getOutputStream();//输出流,客户端到管道//发送消息String content;File file = new File(msg.obj.toString());Log.e("msg.obj.toString()",": "+msg.obj.toString());content = Name.IP + "#$#" + Name.name + "#$#" + file.getName() + "\r\n";Log.e("content", ": "+content );mOutputStream.write(content.getBytes("utf-8"));//先发送了name ip 消息再发送文件内容DataOutputStream fout = new DataOutputStream(fSocket.getOutputStream());DataInputStream fin = new DataInputStream(new FileInputStream(file));//将文件发送出去// 传送文件名字fout.writeUTF(file.getName());Log.e("file.getname()", ": "+file.getName() );fout.flush();// 传送长度fout.writeLong(file.length());Log.e("file.length()", ": "+file.length() );fout.flush();System.out.println("开始传送文件...(大小:" + file.getTotalSpace() + ")");// 传送文件int lengthout = -1;// 读取到的文件长度byte[] buffout = new byte[1024];double curLength = 0;// 循环读取文件,直到结束while ((lengthout = fin.read(buffout)) > 0) {Thread.sleep(10);Log.e(" ", "lengthout: "+lengthout );curLength+=lengthout;Log.e("curlength / length", ": "+curLength+"/"+file.length());fout.write(buffout, 0, lengthout);fout.flush();}System.out.println("传送文件完成");Thread.sleep(5000);byte[] bytes = "EOF".getBytes(Charset.forName("UTF-8"));fout.write(bytes);} catch (IOException | InterruptedException e) {e.printStackTrace();}}}};//Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。Looper.loop();}
接受文件
子线程不断接受从服务端发送过来的文件,也是接受文件名和文件长度和文件字节内容,但会在指定路径新建一个文件来接受传输过来的内容。中途会更新消息列表再去接受
new Thread() {@Overridepublic void run() {super.run();try {while (true){String filename = null;String content = null;DataInputStream dis = new DataInputStream(fSocket.getInputStream());// 从服务器传过来的东西System.out.println("客户端已经链接文件服务");//先传输过来名字和ip和提示文件到达消息mBufferedReader = new BufferedReader(new InputStreamReader(fSocket.getInputStream()));content = mBufferedReader.readLine();Log.e("content", ": "+content );Message handleMsg = new Message();handleMsg.what = 0;handleMsg.obj = content+" position at "+" /storage/emulated/0/Download/";fHandler.sendMessage(handleMsg);filename = dis.readUTF();Log.e("file name", "/storage/emulated/0/Download/"+filename );//根据服务器发送过来的UTF格式的文件名字String destFilePath ="/storage/emulated/0/Download/"+filename;File file = new File(destFilePath);file.createNewFile();// 保存到本地的文件//获取服务器传过来的文件大小Log.e("new position", " "+file.getAbsolutePath() );//显示完整路径double totleLength = dis.readLong();Log.e("length", " "+totleLength );DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));//通过dos往文件里写入内容System.out.println("开始接收:" + totleLength);int length = -1;long recvlength = -1;byte[] buff = new byte[1024];double curLength = 0;try {while((length=dis.read(buff))>0){String str = new String(buff, StandardCharsets.UTF_8);if (str.charAt(0)=='E'&&str.charAt(1)=='O'&&str.charAt(2)=='F'){break;}dos.write(buff, 0, length);Arrays.fill(buff, (byte) 0);//往文件里写入buffLog.e("写入文件的长度: ", " "+length );curLength+=length;//System.out.println("传输进度:"+(curLength/totleLength*100)+"%");System.out.println("传输进度:"+(curLength/totleLength*100)+"%");}System.out.println("传输完成");} catch (Exception ste) {System.out.println("接收文件出错");}}}catch (IOException e) {e.printStackTrace();}}}.start();
中途发送到主线程根据文件消息更新消息列表
fHandler = new Handler() {@Overridepublic void handleMessage(Message handleMsg) {if (handleMsg.what == 0) {//接受到消息后的操作String content = handleMsg.obj.toString();Log.e("recive content", content);String[] arr = content.split("#\\$#");String ip = arr[0];String name = arr[1];String file = arr[2];Log.e("get ", ip+file + name);Msg msg;if (ip.equals(Name.IP)) {Log.e("recive from server", "it is me ");msg = new Msg(name, file, Msg.TYPE_SENT);} else {msg = new Msg(name, file, Msg.TYPE_RECEIVED);}msgList.add(msg);Log.e("TAG", "msg " + msgList.size());adapter.notifyItemInserted(msgList.size() - 1);//当有新消息时,刷新RecyclView中的显示msgRecyclerView.scrollToPosition(msgList.size() - 1);//将RecyclerView定位到最后一行inputTest.setText("");//清空输入框*/}}};
服务端
先列出各个可以监听的ip地址,然后得到运行两个子线程,分别用处理接受消息和文件并再发送给各个客户端
public class MyServer {public static ArrayList<Socket> mSocketList = new ArrayList<>() ;public static ArrayList<Socket> fSocketList = new ArrayList<>() ;public static void main(String[] args) throws SocketException{Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();while (networkInterfaces.hasMoreElements()) {NetworkInterface ni = networkInterfaces.nextElement();for (InterfaceAddress ia : ni.getInterfaceAddresses()) {InetAddress inetAddress = ia.getAddress();if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {System.out.println("ServerSocket可能监听的IP地址: " + inetAddress.getHostAddress());}}}try {//创建服务器SocketServerSocket ss = new ServerSocket(8848);ServerSocket fs = new ServerSocket(18848);while (true){//监听链接Socket s = ss.accept();Socket f = fs.accept();//打印信息System.out.println("ip:"+ s.getInetAddress().getHostAddress() +"加入聊天室");System.out.println("ip:"+ f.getInetAddress().getHostAddress()+" 客户端已经链接文件服务");//将s加入到线程池中mSocketList.add(s);fSocketList.add(f);//启动子线程new Thread(new ServerThread(s)).start();new Thread(new FileThread(f) ).start();}}catch (IOException e){e.printStackTrace();System.out.println("服务器已崩溃");e.printStackTrace();}}
}
接受消息并发送给各个客户端
接受消息,然后发给各个客户端
public class ServerThread implements Runnable {private Socket mSocket = null;private BufferedReader mBufferedReader = null;//构造方法public ServerThread(Socket s)throws IOException{mSocket = s;//输入管道到服务器mBufferedReader = new BufferedReader(new InputStreamReader(s.getInputStream(), "utf-8"));}public void run(){try {String content = null;//循环接受服务器消息,如果没有接收到,说明该客户端下线,将其从线程池中删除while ((content = mBufferedReader.readLine())!=null){System.out.println("ip:"+ mSocket.getInetAddress().getHostAddress()+":"+content);//循环向其他线程发送消息for (Iterator<Socket> it = MyServer.mSocketList.iterator();it.hasNext();) {Socket s = it.next();try {OutputStream os = s.getOutputStream();os.write((content + "\n").getBytes("utf-8"));} catch (SocketException e) {e.printStackTrace();it.remove();}}}}catch (IOException e){System.out.println("接收出错");try {mSocket.close();} catch (IOException e1) {e1.printStackTrace();}MyServer.mSocketList.remove(mSocket);System.out.println("ip:"+ mSocket.getInetAddress().getHostAddress() +"退出聊天室");}}
}
接受文件并发送给各个客户端
接受文件相关信息,在本地新建一个文件,并将接受到的字节流写入文件,然后再将文件相关信息和字节内容发送给各个客户端
public class FileThread implements Runnable{private Socket fSocket = null;private BufferedReader fBufferedReader = null;//构造方法public FileThread(Socket f)throws IOException{fSocket = f;//输入管道到服务器}@Overridepublic void run() {try {while (true) {System.out.println("new"); String filename = null;String content = null;DataInputStream dis = new DataInputStream(fSocket.getInputStream());// 从服务器传过来的东西//先传输过来名字和ip和提示文件到达消息BufferedReader mBufferedReader = new BufferedReader(new InputStreamReader(fSocket.getInputStream()));content = mBufferedReader.readLine()+"\r\n";System.out.println("content"+content);filename=dis.readUTF();//根据客户端发送过来的UTF格式的文件名字File file = new File("D:\\androidstudio\\chatroom\\server\\savefile\\"+filename);System.out.println("filename"+filename);if (!file.exists()) {try {// 新建文件boolean created = file.createNewFile();if (created) {System.out.println("成功创建文件");// 文件成功创建} else {// 文件创建失败,可能是因为权限问题或其他原因}} catch (IOException e) {e.printStackTrace();}}// 保存到本地的文件//获取服务器传过来的文件大小double totleLength = dis.readLong();System.out.println("file length "+totleLength);DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));//通过dos往文件里写入内容System.out.println("开始接收:"+totleLength);int length=-1;byte[] buff= new byte[1024];double curLength = 0;try {while((length=dis.read(buff))>0){String str = new String(buff, StandardCharsets.UTF_8);System.err.println(str);if (str.charAt(0)=='E'&&str.charAt(1)=='O'&&str.charAt(2)=='F'){break;}dos.write(buff, 0, length);Arrays.fill(buff, (byte) 0); //往文件里写入buffcurLength+=length;//System.out.println("传输进度:"+(curLength/totleLength*100)+"%");System.out.println("传输进度:"+(curLength/totleLength*100)+"%");}System.out.println("传输完成");} catch (Exception ste) {System.out.println("接收文件出错"); }for (Iterator<Socket> it = MyServer.fSocketList.iterator();it.hasNext();){ Socket f= it.next();try {DataOutputStream fout = new DataOutputStream(f.getOutputStream());DataInputStream fin = new DataInputStream(new FileInputStream(file));fout.write(content.getBytes("utf-8"));System.out.println("content: "+content);//将文件发送出去// 传送文件名字fout.writeUTF(file.getName());System.out.println("file.getName() "+file.getName());fout.flush();// 传送长度fout.writeLong(file.length());System.out.println("file.length() "+file.length());fout.flush();System.out.println("开始传送文件...(大小:" + file.getTotalSpace() + ")");// 传送文件int lengthout = -1;// 读取到的文件长度byte[] buffout = new byte[1024];curLength = 0;// 循环读取文件,直到结束while ((lengthout = fin.read(buffout)) > 0) {Thread.sleep(4);//System.out.println(" lengthout: "+lengthout );curLength+=lengthout;System.out.println("curlength / length: "+curLength+"/"+file.length());fout.write(buffout, 0, lengthout);fout.flush();}System.out.println("传送文件完成");Thread.sleep(1000);byte[] bytes = "EOF".getBytes(Charset.forName("UTF-8"));fout.write(bytes);}catch (Exception e) {System.out.println("传输意外");}} }}catch (IOException e) {System.out.println("接收出错");try {fSocket.close();} catch (IOException e1) {e1.printStackTrace();}MyServer.fSocketList.remove(fSocket);System.out.println("ip:"+ fSocket.getInetAddress().getHostAddress() +"文件传输结束");}}
}
相关文章:
实现一个聊天室可发送消息语音图片视频表情包(任意文件)
文章目录 如何跑通代码仓库地址客户端登录发送消息接受消息发送文件接受文件 服务端接受消息并发送给各个客户端接受文件并发送给各个客户端 如何跑通 将手机和电脑都连自己的热点先运行服务器得到可监听的地址更新客户端安卓消息线程和文件线程的socker目标地址为可监听地址然…...
【SpringMVC 】什么是SpringMVC(一)?如何创建一个简单的springMvc应用?
文章目录 SpringMVC第一章1、什么是SpringMVC2、创建第一个SpringMVC的应用1-3步第4步第5步第6步7-8步3、基本语法1、进入控制器类的方式方式1:方式2:方式3:方式4:方式5:2、在控制器类中取值的方式方式1:方式2:方式3:方式4:方式5:方式6:超链接方式7:日期方式8:aja…...
【配置】IT-Tools部署
github地址 docker运行如下,记得打开云服务器的9090端口 docker run -d --name it-tools --restart unless-stopped -p 9090:80 corentinth/it-tools:latestip:9090查看,很香大部分工具都有...
【Python】如何训练模型并保存本地和加载模型
这个年纪的我们 爱情跟不上分开的节奏 这个年纪的我们 更珍惜难得的自由 这个年纪的我们 比起从前更容易感动 这个年纪的我们 徘徊在理想与现实之中 🎵 齐一《这个年纪》 逻辑回归是一种常用的分类算法,能够根据输入特征预测目标变…...
浅谈如何利用 AI 提高内容生产效率?|TodayAI
在数字化时代,内容的创建和分发速度变得尤为关键。人工智能(AI)技术提供了加速这一过程的可能性,不仅提升了生产效率,还改善了内容的质量和受众的接受度。本文深入探讨AI如何在内容生成、分发与推广,以及内…...
毕业论文答辩PPT怎么做?推荐3个ai工具帮你一键生成答辩ppt
在我原本的认知里面,答辩PPT是要包含论文各个章节的,在答辩时需要方方面面都讲到的,什么摘要、文献综述、实证分析、研究结果样样不落。但是,这大错特错! 答辩PPT环节时长一般不超过5分钟,老师想要的答辩P…...
力扣 5-11
704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。 这道题目的前提是数组为有序数组,同时题目还强…...
redisson 使用脚本实现判断元素不在队列中则插入的原子操作
脚本逻辑: 取出队列所有元素遍历元素查找值是否存在不存在则推入 final String scriptText """local valuesInTarget redis.call(lrange, KEYS[1], 0, -1);local index 0;for i, v in ipairs(valuesInTarget) doif v value thenindex ibreake…...
LLaMA详细解读
LLaMA 是目前为止,效果最好的开源 LLM 之一。精读 LLaMA 的论文及代码,可以很好的了解 LLM 的内部原理。本文对 LLaMA 论文进行了介绍,同时附上了关键部分的代码,并对代码做了注释。 摘要 LLaMA是一个系列模型,模型参…...
纯血鸿蒙APP实战开发——页面间共享组件实例的案例
介绍 本示例提供组件实例在页面间共享的解决方案:通过Stack容器,下层放地图组件,上层放Navigation组件来管理页面,页面可以共享下层的地图组件,页面中需要显示地图的区域设置为透明,并参考触摸交互控制&am…...
华为机考入门python3--(22)牛客22- 汽水瓶
分类:数字 知识点: 整除符号// 5//3 1 取余符号% 5%3 2 题目来自【牛客】 import sysdef calc_soda_bottles(n):if n 0: # 结束输入,不进行处理returnelse:# 循环进行汽水换算total_drunk 0 # 记录总共喝了多少瓶汽水while…...
Xilinx 千兆以太网TEMAC IP核简介
Xilinx 公司提供了千兆以太网MAC控制器的可参数化LogiCORET™IP解决方案,通过这个IPCore可以实现FPGA与外部网络物理层芯片的互连。基于Xilinx FPGA 的以太网设计,大大降低了工程的设计复杂度,缩短了开发周期,加快了产品的面市速度…...
激光测径仪在胶管生产中扮演着什么角色?
关键词:激光测径仪,胶管,胶管测径仪,在线测径仪 胶管生产的基本工序为混炼胶加工、帘布及帆布加工、胶管成型、硫化等。不同结构及不同骨架的胶管,其骨架层的加工方法及胶管成型设备各异。 全胶胶管因不含骨架层,只需使用压出机压出胶管即可&…...
数据结构与算法===递归
文章目录 定义适用场景爬楼梯代码实现 小结 定义 递归(Recursion)是指函数的自身调用。 这个算法演变为了程序员之间的梗,所表达的意思近似于“套娃”,表示不断重复引用别人的话从而产生循环。 适用场景 这个应该很多的,像一些树的遍历&am…...
面试官:BIO、NIO 和 AIO 有什么区别?
BIO(Blocking I/O)、NIO(Non-blocking I/O)和AIO(Asynchronous I/O)是Java中用于处理I/O操作的三种不同的编程模型. BIO适用于连接数较少的情况,NIO适用于连接数较多但连接活跃度不高的情况&…...
HTML:元素属性详解及代码示例
引言 HTML(HyperText Markup Language)是构建网页和网页应用的基石。通过使用各种元素和属性,我们可以创建结构化、样式化并具有交互性的内容。本文将深入探讨HTML元素的属性,并提供实用的代码示例。 HTML元素属性概述 HTML元素…...
【Flask 系统教程 5】视图进阶
类视图 在 Flask 中,除了使用函数视图外,你还可以使用类视图来处理请求。类视图提供了一种更为结构化和面向对象的方式来编写视图函数,使得代码组织更清晰,并且提供了更多的灵活性和可扩展性。 创建类视图 要创建一个类视图&am…...
代码训练LeetCode(17)存在重复元素
代码训练(17)LeetCode之存在重复元素 Author: Once Day Date: 2024年5月7日 漫漫长路,才刚刚开始… 全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客 参考文章: 219. 存在重复元素 II - 力扣(LeetCode)力扣 (LeetCode) 全球…...
运营模型—归因分析(Attribution Analysis)
运营模型—归因分析(Attribution Analysis) 随着互联网技术和业务的发展,广告投放相关的业务也随之兴起。那么广告投放的效果评估也就随之而来。广告的投放一般都是收费模式,所以选中的渠道商的好坏直接和自己的利益挂钩。于是,「归因分析」便最早应用在了广告投放行业。(…...
我必须要吹一波MATLAB 2024a,太牛逼了!|福利:附安装教程及下载地址
最近逛MATLAB官网,发现MATLAB 2024a版本已经Pre-release了,翻了下release note,不得不感叹,实在是太强了! 这次重点更新了四个工具箱: Computer Vision Toolbox Deep Learning Toolbox Instrument Contro…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
