已阅读5页,还剩19页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux下使用套接口和GTK编写网络通信程序第一章、基础知识1.1 套接口1.1.1基础知识套接口(socket)就是网络进程的ID,其可以简单的理解为网络地址(ip地址)和端口号。套接口分两种,流式套接口使用TCP协议,数据报套接口使用UDP协议。在Linux中套接口的操作类似于文件描述符,可以像操作文件一样操作他们。在Linux中使用套接口发送数据要注意字节顺序的问题,在网络传输中,是以高字节在前(big endian)的方式传送的,而本机解析这些数据,是和CPU的结构有关,在传输到网络之前,需将套接口数据结构sockaddr_in中的网络地址和端口转换为高字节在前的形式。套接口的基本操作有绑定、连接、监听、应答、发送、接受、关闭等,以客户/服务器为主的网络机制其用socket进行TCP服务的简化图1-1。一般给套接口分配的文件描述符从3开始,0为默认输入,1为默认输出,2为错误默认输出,这三个描述符固定分配给系统使用,如果将套接口文件描述符设置成相应的1/2/3,则获得的数据则输出到相应的地方。图1-11.1.2 关于I/O使用套接口主要进行数据传输,不免要与I/O打交道。其I/O操作主要有四种模型:阻塞式,非阻塞,多路复用以及信号驱动。本文的程序使用的是阻塞式的I/O,其简单但效率相对较低。阻塞在套接口应用中很重要,处理不好会导致双方均处于阻塞状态,造成死锁。比如客户端进行read操作,但是对方迟迟不发送信息,导致read一直没有返回值,程序便会一直等待。1.2 GTKGTK (Gimp Tool Kit) 为GNOME中使用的开源界面开发程序。相应的,在KDE中主要使用的是QT。GTK使用了事件触发的结构处理用户响应,其开发包包括GDK,支持包含C之内的多种编程语言1.3 Linux下的c语言编写这里Linux下的c语言编辑主要使用vi,编译程序使用gcc。Gcc编译过程中,有静态连接库等问题需要注意,使用的命令为gcc -std=c99 pkg-config -cflags -libs gtk+-2.0 -lpthread client.c -o clientgcc -std=c99 pkg-config -cflags -libs gtk+-2.0 -lpthread service.c -o service在处理字符串的时候,多应用stdlib.h和string.h里的标准库函数,区分指向常量区域的字符串指针以及字符串数组之间的区别,明确字符串数组的赋值方法(单个元素赋值或用strcat stpcpy),在Linux条件下,没有itoa函数将整型变为字符型,可以使用sprintf函数。1.4 多线程一个进程(process)派生另一个进程称为多进程,其相比单一进程具有较高的灵活性,能够更有效地利用CPU,但是其代价为较大的内存占用以及麻烦的进程间通信,在这样的前提下产生了多线程。多进程和多线程的区别在于,多线程中的子线程可以访问共享内存区域中的数据,简化了线程间的通信,提高了程序特别是用户界面程序的响应。但是在使用线程中要注意同时访问变量的问题,应该使用一定的机制(比如互斥锁mutex),让各个子线程以串行的方式访问并修改内存中的数据,避免出现内存使用冲突的问题。多个线程能够共享同一个进程代码段,共有数据(用来通讯),进程打开的文件描述符,信号的处理器,进程当前目录,进程用户ID等。当然每一个线程也有其独有的东西:线程ID,寄存器值,堆栈,错误返回码,信号屏蔽码和优先级。创建线程需要pthread.h头文件,连接时要使用libpthread.a的库,这里简单讲解几个和线程有关的函数。Int pthread_create(pthread_t *tid, const pthread_attr_r *attr, void *(*func)(void*), void *arg)四个输入参数依次为线程ID标识符,创建线程的属性,线程将要执行的函数-返回void类型指针,以及传递给函数的参数(这里,参数需是void类型的,int等类别的参数需要进行转换,多参数传递建议使用结构体)。Int pthread_join(pthread_t tid, void *status)等待tid的线程终止,然后再执行下面的命令。Status指针内将保存线程的返回值。pthread_t pthread_self(void)返回线程ID。pthread_cancel结束其他它由同一个进程产生的线程,这里注意,当子线程阻塞在I/O时(比如,read等待),是不可以用这个函数退出次线程的,这时就要考虑使用非阻塞的I/O获得较高的控制权和灵活性。pthread_exit结束线程本身。在使用Pthread时避免线程的资源在线程结束时不能得到正确释放,从而避免产生潜在的内存泄漏问题,在对待线程结束时,要确保该线程处于detached状态(pthread_detach(),否着就需要调用 pthread_join()函数来对其进行资源回收。线程可以通过自身执行结束来结束,也可以通过调用pthread_exit()来结束线程的执行。另外,线程甲可以被线程乙被动结束,通过调用pthread_cancel()来达到目的,cancel并不等待线程终止, 它仅仅是提出请求。可以用如下两个函数设置本线程的应答属性。int pthread_setcancelstate(int state, int *oldstate)state 取值可以是PTHREAD_CNACEL_ENABLE,这个值允许线程接收取消请求。int pthread_setcanceltype(int type, int *oldtype)设置取消的类型,type取值可以是PTHREAD_CANCEL_ASYNCHRONOUS,它将使得线程在接收到取消请求后立即处理。在默认的情况下,线程启动时的取消状态为PTHREAD_CNACEL_ENABLE,取消类型为PTHREAD_CANCEL_DEFERRED。1.5 IP地址和端口在笔者处的教育网段,每台电脑几乎都可以分配一个独立的IP地址,每一个学校的IP地址是在一段地址之间。一般一台主机只有一个IP地址,除非有多块网卡。IP地址分为内网地址和外网地址,内网地址用于局域网广播,多是C网。在网络上唯一确定一个传输数据的地址,需要IP地址和端口的支持。在这里的通信程序中,服务器使用固定的IP和端口,客户端使用自己本机的IP和随机分配的端口进行访问。为避免冲突,一般端口设置在1024之上较为合理。在Linux下运行ifconfig可以得到本机的以太网卡IP和本机IP。无论系统是否接入网络,本机网络这个设备总是存在的,除非你在内核编译的时候禁止了网络支持,这是一个称为回送设备的特殊设备,它自动由Linux 配置以提供网络的自身连接。IP地址127.0.0.1是一个特殊的回送地址(即默认的本机地址),您可以在自己的系统上用telnet对IP地址127.0.0.1进行测试。如果有inetd进程在运行的话您会从自己的机器上获得登录提示符。Linux 可以利用这个特征在进程与仿真网络之间进行通信。1.6 TCP&UDPTCP为面向连接的协议,其保证数据能够准确且按顺序地到达,在Socket连接选择类型为SOCK_STREAM时即使用此协议。由于其是面向连接的,通信时需要listen-connect的三步握手协议。在发送数据时,先将数据保存在缓冲区,等待对方对发送的数据进行确认后才清楚缓冲区中的数据。其实现为数据流类型的服务,需要应用程序控制数据的边界。UDP为面向无连接的协议,其保证数据的及时性,但对消息的尺寸加以限制,且没有对准确性的保证,在Socket连接选择类型为SOCK_DGRAM时即使用此协议。其通信时不需要握手协议,发送数据时直接将数据从应用缓冲区复制到系统缓冲区中,然后子程序返回。数据包的生成由UDP协议实现。其缓冲区具有一定大小,为UDP数据报的最大长度。其实现的是数据报类型服务,采用面向记录的协议。二者比较来看,UDP服务器通常是以循环方式对多个客户进行服务的,TCP通常使用轮询或其他多线程的方式服务多个客户。第二章、基本实现原理2.1 基本实现结构基本的实现结构如图2-1所示,在发送登录,刷新信息时,使用互斥锁的多线程,保证当前信息的准确性,如图B;在发送消息的时候,使用直接转发的方式,如图A。但这样会有一个问题,当用户数量较多时,发送的登录和刷新信息会造成其他用户的白白等待,如图C所示。程序内部流程框图如图2-2图2-1图2-22.2 更多的想法一个改进该问题的想法如下图所示,使用两个服务器,一个作为登录使用,另一个专门作为通信交换使用,两个服务器之间进行同步。当然这种实践叫前者复杂,这里没有完成其代码。图2-3第三章、部分关键代码解释3.1 服务器代码3.1.1 关于互斥锁以下代码首行和末行分别取得互斥锁的所有权和释放有权。其他试图对数据加锁的线程将被阻塞直到当前线程释放对该数据的锁。pthread_mutex_lock(&mutex);USER_INFOin_unpack.send_from.stat = 0;USER_INFOin_unpack.send_from.sd = 0;in_unpack.tag = LOGOUT_AWK;do_pack(&in_unpack,in_pack);flag += write(new_sdin_sd,in_pack,sizeof(in_pack);sleep(0.1);pthread_mutex_unlock(&mutex);3.1.2 关于线程的参数传递pthread_create(&THREADtmp_index,NULL,do_service,(void*)flag_find);将int格式的flag_find变为void格式的指针传递给函数do_service。3.1.3 关于数据结构struct msg_pack /message pack struct int tag; /msg type char size:2 int send_from; /msg send from char size:8 int send_to; /msg send to char size:8 char key64; /key used for login char size:8 char msgPACK_MSG;/msg content char size:998;定义了一个包含五个元素的结构体用于write和read时的数据结构。3.2 用户代码3.2.1 关于显示文本框内容的添加gtk_text_buffer_get_end_iter(text_buffer,&iter);gtk_text_buffer_insert(text_buffer,&iter,this is it,-1);3.2.2防止缓存溢出每次在进行对数组的赋值时,检查是否出现溢出,消除潜在的core错误。char format_cash9;format_char(send_to,format_cash,8);int tmp_send_from = atoi(format_cash);从文本框中读入数据char *login_ip = gtk_entry_get_text(GTK_ENTRY(message_entry_ip);第四章、运行调试结果编译service.c和client.c两个主文件,出现若干个警告,编译通过,调试基本成功。BUG报告:当服务器先于客户端关闭时会导致客户端的异常退出。程序运行截图如下。图4-1参考文献宋国伟,GTK+2.0编程范例张威,Linux网络编程教程Warren W. Gay,实战Linux Socket编程附录define.h源代码#include#include#include#include#include#include#include#include#include#include#include#include#define MAXUSER 4#define BUFSIZE 100#define MYPORT 3490#define BACKLOG 10#define NO 0#define LOGIN 1#define LOGIN_AWK 2#define LOGIN_NEG 3#define REFRESH 4#define REFRESH_AWK 5#define REFRESH_NEG 6#define LOGOUT 7#define LOGOUT_AWK 8#define LOGOUT_NEG 9#define SEND 10#define PACK 1024#define PACK_HEAD 26#define PACK_MSG 998struct user_info /user info structint sd; /socket idint stat; /on line?int id; /user idchar key12; /user keychar time128;/user last login time;struct user_info USER_INFOMAXUSER = -1,0,0,0,-1,0,1,11111111,-1,0,2,2,-1,0,3,3,;struct msg_pack /message pack structint tag; /msg type char size:2int send_from; /msg send from char size:8int send_to; /msg send to char size:8char key64; /key used for login char size:8char msgPACK_MSG;/msg content char size:998;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;int new_sdMAXUSER;int tmp_index = -1;int total_online = 0;/*/build socket,init,and bind it to a portint build_ear(int ip)/build socketint sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd 0)printf(cannot build socketn);return -1;struct sockaddr_in my_addr;struct sockaddr_in their_addr;/initmy_addr.sin_family = AF_INET;my_addr.sin_port = htons(MYPORT);my_addr.sin_addr.s_addr = htonl(ip);bzero(&(my_addr.sin_zero),8);/bindint ret = bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr);if(ret 0)printf(cannot bind socket with portn);return -1;return 0;int parse_sig(char *in,char *flag,char *data)char *p = NULL;int i = 0;p = strtok(in,SPLIT_TAG);flag = stpcpy(flag,p);p = strtok(NULL,SPLIT_TAG);data = stpcpy(data,p);return 0;*/ used for pick numberint cut_char_int(char *in,int *out,int beg,int out_len)int in_len = strlen(in);char tmp1024;if(in_len beg+out_len | beg 0)return -1;int i = 0;for(i=beg; i=beg+out_len-1; i+)*(tmp+i-beg) = *(in+i);*(tmp+out_len) = 0;*out = atoi(tmp);return 0;/ used for pick key,ignore blankint cut_char_char(char *in,char *out,int beg,int out_len)int in_len = strlen(in);if(in_len beg+out_len | beg 0)return -1;int i = beg;int j = 0;for(i=beg; i=beg+out_len-1; i+)if(*(in+i) = )continue;*(out+j) = *(in+i);j+;*(out+j) = 0;return 0;/ used for pick msgint cut_char_talk(char *in,char *out)int i = PACK_HEAD;for(; *(in+i) != 0; i+)*(out+i-PACK_HEAD) = *(in+i);*(out+i-PACK_HEAD) = 0;return 0;/ used for format char to 8int format_char(char *in,char *out,int size)int i = 0;int con = 0;for(; *(in+i) != 0 & itag,in-send_from,in-send_to,in-key,in-msg);return flag;/ unpack data structint do_unpack(char *in,struct msg_pack *out)int len = strlen(in);if(len tag,0,2);flag_cut += cut_char_int(in,&out-send_from,2,8);flag_cut += cut_char_int(in,&out-send_to,10,8);flag_cut += cut_char_char(in,out-key,18,8);flag_cut += cut_char_talk(in,out-msg);if(flag_cut != 0)return -1;return 0;/ do service in threadvoid *do_service(void* args)int in_sd = (int) args;char inPACK;/g_printf(in thread %dn,new_sdin_sd);/total_online+;struct msg_pack in_unpack ;while(read(new_sdin_sd,in,PACK) 0)do_unpack(in,&in_unpack);g_printf(tread msg %sn,in);int flag = 0;char in_packPACK;switch(in_unpack.tag)case LOGIN: g_printf(tloginn); pthread_mutex_lock(&mutex); if(in_unpack.send_from = MAXUSER) in_unpack.tag = LOGIN_NEG; stpcpy(in_unpack.msg,wrong user id); else if(strcmp(USER_INFOin_unpack.send_from.key,in_unpack.key) != 0) in_unpack.tag = LOGIN_NEG; stpcpy(in_unpack.msg,wrong user key); else if(USER_INFOin_unpack.send_from.stat != 0) in_unpack.tag = LOGIN_NEG; stpcpy(in_unpack.msg,user already login); else in_unpack.tag = LOGIN_AWK; USER_INFOin_unpack.send_from.stat = 1; USER_INFOin_unpack.send_from.sd = new_sdin_sd; ; do_pack(&in_unpack,in_pack); flag += write(new_sdin_sd,in_pack,sizeof(in_pack); pthread_mutex_unlock(&mutex);break;case LOGOUT:pthread_mutex_lock(&mutex);USER_INFOin_unpack.send_from.stat = 0;USER_INFOin_unpack.send_from.sd = 0;in_unpack.tag = LOGOUT_AWK;do_pack(&in_unpack,in_pack);flag += write(new_sdin_sd,in_pack,sizeof(in_pack);sleep(0.1);pthread_mutex_unlock(&mutex);break;case REFRESH: g_printf(trefreshn); pthread_mutex_lock(&mutex); for(int i=0; iMAXUSER; i+) if(USER_INFOi.stat = 1) char tmp_id12; sprintf(tmp_id,%d,USER_INFOi.id); strcat(in_unpack.msg,tmp_id); strcat(in_unpack.msg,n); ; ; in_unpack.tag = REFRESH_AWK; do_pack(&in_unpack,in_pack); flag += write(new_sdin_sd,in_pack,sizeof(in_pack); sleep(0.1); pthread_mutex_unlock(&mutex); ;break;case SEND: g_printf(tsendn); if(in_unpack.send_to = MAXUSER) g_print(tsend to wrong usern); else if(USER_INFOin_unpack.send_to.stat != 1) else do_pack(&in_unpack,in_pack); flag += write(USER_INFOin_unpack.send_to.sd,in_pack,sizeof(in_pack); ;break;if(flag 0)g_print(loop wrong flag %dn,flag);break;g_printf(tend of this clientn);total_online-;new_sdin_sd = -1;USER_INFOin_unpack.send_from.stat = 0;close(new_sdin_sd);service.c源代码#include#include#include#include#include#include#include#include#include#include#includedefine.hpthread_t THREADMAXUSER;int main(int argc, char *argv)gint sd;gchar buf1024;struct sockaddr_in *sin;gint slen;for(int i=0; iMAXUSER; i+)new_sdi = -1;sd = socket(AF_INET,SOCK_STREAM,0);if(sd sin_family = AF_INET;sin-sin_port = htons(MYPORT);sin-sin_addr.s_addr = INADDR_ANY;slen = sizeof(struct sockaddr_in);if(bind(sd,(struct sockaddr *)sin,slen)0)g_print(cannot bindn);return -1;while(1)if(listen(sd,8)0)g_print(cannot listenn);int flag_accept = -1;int flag_find = -1;flag_accept = accept(sd,(struct sockaddr*)sin,&slen);if(flag_accept 0)continue;for(int i=0; iMAXUSER; i+)if(new_sdi = -1)flag_find = i;new_sdi = flag_accept;break;if(flag_find = -1)sprintf(buf,too many users now);write(flag_accept,buf,1024);close(flag_accept);elsetotal_online+;g_printf(new thread create id %dn totoal %d user on linen,new_sdflag_find,total_online);pthread_create(&THREADtmp_index,NULL,do_service,(void*)flag_find);g_print(end alln);/close(sd);/g_free(sin);client.c源代码#include#include#include#include#include#include#includedefine.h/ globalgint sd;int user_id;int refresh_flag = 0;pthread_t GETMSG;static GtkWidget *id_entry;static GtkWidget *key_entry;static GtkWidget *send_button, *refresh_button, *login_button;static GtkWidget *login_window;static GtkWidget *message_entry_ip, *message_entry_id, *message_entry_key;static GtkWidget *message_entry_to, *message_entry_msg;static GtkWidget *text;static GtkTextBuffer *text_buffer;static GtkTextIter iter;struct sockaddr_in service_addr;void on_delete_main_win()gtk_main_quit();void on_delete_login_win()gtk_widget_set_sensitive(login_button,TRUE);gtk_widget_destroy(login_window);void on_open_login_win();void on_send_msg_but();void on_send_refresh_but();void on_send_login_but();void *get_msg();int main(int argc,char* argv)GtkWidget *window;GtkWidget *vbox, *hbox;GtkWidget *view;GtkWidget *label_1, *label_2;/ main window gtk_init(&argc,&argv);window = gtk_window_new(GTK_WINDOW_TOPLEVEL);gtk_window_set_title(GTK_WINDOW(window),client);g_signal_connect(G_OBJECT(window),delete_event,G_CALLBACK(on_delete_main_win),NULL);gtk_container_set_border_width(GTK_CONTAINER(window),10);/ vertical boxvbox = gtk_vbox_new(FALSE,0);gtk_container_add(GTK_CONTAINER(window),vbox);login_button = gtk_button_new_with_label(login);gtk_box_pack_start(GTK_BOX(vbox),login_button,FALSE,FALSE,5);g_signal_connect(G_OBJECT(login_button),clicked,G_CALLBACK(on_open_login_win),NULL);view = gtk_scrolled_window_new(NULL,NULL);gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(view),GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);text = gtk_text_view_new();gtk_box_pack_start(GTK_BOX(vbox),view,TRUE,TRUE,5);gtk_container_add(GTK_CONTAINER(view),text);text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text);/ send to sb. sth.label_1 = gtk_label_new(send to);gtk_box_pack_start(GTK_BOX(vbox),label_1,FALSE,FALSE,5);message_entry_to = gtk_entry_new();gtk_box_pack_start(GTK_BOX(vbox),message_entry_to,FALSE,FALSE,5);label_2 = gtk_label_new(your word);gtk_box_pack_start(GTK_BOX(vbox),label_2,FALSE,FALSE,5);message_entry_msg = gtk_entry_new();gtk_box_pack_start(GTK_BOX(vbox),message_entry_msg,FALSE,FALSE,5);/ button in boxhbox = gtk_hbox_new(FALSE,0);gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,5);refresh_button = gtk_button_new_with_label(refresh!);gtk_box_pack_start(GTK_BOX(hbox),refresh_button,FALSE,FALSE,5);g_signal_connect(G_OBJECT(refresh_button),clicked,G_CALLBACK(on_send_refresh_but),NULL);send_button = gtk_button_new_with_label(send!);gtk_box_pack_start(GTK_BOX(hbox),send_button,FALSE,FALSE,5);g_signal_connect(G_OBJECT(send_button),clicked,G_CALLBACK(on_send_msg_but),NULL);gtk_widget_set_sensitive(send_button,FALSE);gtk_widget_set_sensitive(refresh_button,FALSE);gtk_widget_show_all(window);gtk_main();return FALSE;/ open login windowvoid on_open_login_win()GtkWidget *send_login_button;GtkWidget *vbox;GtkWidget *hbox_1, *hbox_2, *hbox_3;GtkWidget *label_1, *label_2, *label_3;/ set activegtk_widget_set_sensitive(login_button,FALSE);login_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);gtk_window_set_title(GTK_WINDOW(login_window),login);gtk_container_set_border_width(GTK_CONTAINER(login_window),10);/ vertical boxvbox = gtk_vbox_new(FALSE,0);gtk_container_add(GTK_CONTAINER(login_window),vbox);/ box 1hbox_1 = gtk_hbox_new(FALSE,0);gtk_box_pack_start(GTK_BOX(vbox),hbox_1,FALSE,FALSE,5);label_1 = gtk_label_new(input service ip);gtk_box_pack_start(GTK_BOX(hbox_1),label_1,FALSE,FALSE,5);message_entry_ip = gtk_entry_new();gtk_box_pack_start(GTK_BOX(hbox_1),message_entry_ip,FALSE,FALSE,5);/ box 2hbox_2 = gtk_hbox_new(FALSE,0);gtk_box_pack_start(GTK_BOX(vbox),hbox_2,FALSE,FALSE,5);label_2 = gtk_label_new(input user id);gtk_box_pack_start(GTK_BOX(hbox_2),label_2,FALSE,FALSE,5);message_entry_id = gtk_entry_new();gtk_box_pack
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年企业文化专员人员招聘面试参考题库及答案
- 2025年商务拓展经理招聘面试参考题库及答案
- 2025年风险管理师招聘面试参考题库及答案
- 2025年审计师人员招聘面试参考题库及答案
- 2025年金融风控经理招聘面试参考题库及答案
- 2025年医疗行政助理招聘面试参考题库及答案
- 2025年可持续发展专员招聘面试题库及参考答案
- 2025年医护管理员招聘面试参考题库及答案
- 2025年香港股票交易分析师招聘面试参考题库及答案
- 2025年培训开发专员招聘面试参考题库及答案
- 小学六年级数学上册应用题100道(全)-及答案
- 2022年河北省普通高中学业水平合格性考试语文试题(解析版)
- 2023年延安市安塞区社区工作者招聘考试真题
- 驾校教练员安全培训
- 全国主要水文站点及雨量观测分布和代码
- 2023年贵州省遵义市中考地理试卷真题(含答案)
- 儿童文学教程第2版(学前教育专业)PPT全套完整教学课件
- 中国绿色建筑三星认证标准
- 《领导力》读书笔记
- 2023年江苏省初中物理竞赛试卷
- GB/T 20967-2007无损检测目视检测总则
评论
0/150
提交评论