Linux 网络编程(UDP模型,libevent库使用)
创始人
2024-05-30 16:25:02
0

1.ctags的使用

安装命令:sudo apt install exuberant-ctags

要使用ctags需要在当前目录生成 tags 文件,可以组织目录内所有.c间函数调用关系

生成方法:1.在项目目录下输入命令:ctags ./* -R

2.在任意一个文件内使用 ctrl+p

一些快捷命令:

命令

作用

ctrl + ]

光标放置于调用函数上,跳转至函数定义位置

ctrl + t

返回到此前跳转位置

ctrl + o

在文件左边列出文件列表,再次 ctrl+o关闭

F4

在文件右边列出函数列表,再次 F4 关闭

2.UDP通信的实现

1.使用到的函数

1.recvfrom函数:包含在头文件

函数原型:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);作用:接受信息,涵盖了客户端或者服务器的信息参数解释:参数sockfd:服务器socket参数buf:缓冲区参数len:缓冲区大小参数src_addr:对端的结构体对象参数addrlen:结构体大小返回值:成功返回接受的数据字符个数,失败返回-1,返回0,表示对端关闭

2.sendto函数:包含在头文件

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);作用:发送信息,涵盖了客户端或者服务器的信息参数解释:参数sockfd:服务器socket参数buf:缓冲区参数len:缓冲区大小参数src_addr:目标结构体对象参数addrlen:结构体大小返回值:成功返回写出的数据字符个数,失败返回-1,返回0,表示对端关闭

3.fgets函数:包含在头文件

函数原型:

char *fgets(char *s, int size, FILE *stream); 作用:从stream流中读取size个字符存储到字符指针变量s所指向的内存空间参数解释:参数s:表示存储字符的空间地址参数size:读取字符串的长度参数stream:表示从何种流中读取,可以是标准输入流stdin,也可以是文件流
UDP客户端实现代码
#include
#include
#include
#include
#includeint main()
{int cfd = socket(AF_INET, SOCK_DGRAM, 0);struct sockaddr_in client_addr;client_addr.sin_family = AF_INET;client_addr.sin_port = htons(12345);inet_pton(AF_INET, "127.0.0.1", &client_addr.sin_addr.s_addr);char buf[1024] = {0};while(fgets(buf, 1024, stdin) != NULL){//参数5使用的是服务器的socket信息int n = sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&client_addr,sizeof(client_addr)); memset(buf, 0, 1024);recvfrom(cfd, buf, 1024, 0, NULL, 0);printf("服务器发来消息:%s", buf);}close(cfd);return 0;
}
UDP服务器实现代码
#include
#include
#include
#include
#include
#include
#includeint main()
{int lfd = socket(AF_INET, SOCK_DGRAM, 0);//在这里使用sock_dgramstruct sockaddr_in sev_addr;bzero(&sev_addr, sizeof(sev_addr));sev_addr.sin_family = AF_INET;sev_addr.sin_port = htons(12345);sev_addr.sin_addr.s_addr = htonl(INADDR_ANY);bind(lfd, (struct sockaddr*)&sev_addr, sizeof(sev_addr));printf("wait connecting.....\n");struct sockaddr_in client_addr;char buf[1024] = {0};int i;while(1){int client_len = sizeof(client_addr);//接受客户端的同时,参数5可以获取到客户端的socket信息int n = recvfrom(lfd, buf, 1024, 0, (struct sockaddr*)&client_addr, &client_len);printf("客户端发来消息:%s",buf); for(i = 0;i < n;i++);{buf[i] = toupper(buf[i]);}//给获取到的客户端发送消息sendto(lfd, buf, n, 0, (struct sockaddr*)&client_addr, sizeof(client_addr));}close(lfd);return 0;
}    

3.本地套接字

概念:进程间通信的一种方式是使用UNIX套接字,人们在使用这种方式时往往用的不是网络套接字,而是一种称为本地套接字的方式,就是创建一个伪文件访问绑定结构体成员中的path作为一个通信的介质

1.使用到的函数以及结构体

1.socket_un结构体

结构体原型:

struct sockaddr_un{sa_family_t  sun_family;    //地址结构体类型,只能是AF_LOCAL或AF_UNIXchar         sun_path[108];    //本地文件的路径通常放在/tmp下}在sockaddr_un需要使用strcpy()函数把伪文件名赋值给岑有sun_path

2.offset函数:包含在头文件

函数原型:

size_t offsetof(type, member);作用:计算结构体成员在内存中的偏移量参数解释:参数type:结构体名称参数member:是结构体的内部成员返回值:返回成员在结构体中的偏移量

3.unlink函数:包含在头文件

函数原型:

int unlink(const char *pathname);作用:从文件系统中删除一个名称。如果名称是文件的最后一个连接,并且没有其它进程将文件打开
,名称对应的文件会实际被删除参数解释:需要解除连接的文件名
本地套接字通信的客户端代码实现
#include
#include
#include
#include
#include
#include
#include
#include
#include#define CLIENT_FILE "client.socket"
#define SERVER_FILE "serv.socket"int main()
{int cfd = socket(AF_UNIX, SOCK_STREAM, 0);struct sockaddr_un client_addr;bzero(&client_addr, sizeof(client_addr));client_addr.sun_family = AF_UNIX;strcpy(client_addr.sun_path, CLIENT_FILE);//因为本地套接字不会自动绑定socket端口号和ip地址信息,所以需要使用bind函数进行绑定int len = offsetof(struct sockaddr_un, sun_path) + strlen(client_addr.sun_path);unlink(CLIENT_FILE);bind(cfd, (struct sockaddr*)&client_addr, len);    //需要手动绑定数据信息,在本地套接字是不会自动绑定结构体信息的struct sockaddr_un serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sun_family = AF_UNIX;strcpy(serv_addr.sun_path, SERVER_FILE);len = offsetof(struct sockaddr_un, sun_path) + strlen(serv_addr.sun_path);//因为是需要连接的是服务器,所以需要传服务器的文件套接字connect(cfd, (struct sockaddr*)&serv_addr, len);char buf[1024] = {0};while(fgets(buf, 1024, stdin) != NULL){write(cfd, buf, strlen(buf));int ret = read(cfd, buf, 1024);if(ret == 0 || ret == -1){printf("error\n");break;}printf("服务器%s发来消息:%s\n", serv_addr.sun_path, buf);}close(cfd);return 0;
}
本地通信的套接字服务器代码实现
#include
#include
#include
#include
#include
#include
#include#define SERV_FILE "serv.socket"int main()
{int lfd = socket(AF_UNIX, SOCK_STREAM, 0);struct sockaddr_un serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sun_family = AF_UNIX;strcpy(serv_addr.sun_path, SERV_FILE);//把文件路径复制到结构体成员//因为结构体sockaddr_un具有首部2字节长度,所以需要使用该函数计算path的实际长度    int len = offsetof(struct sockaddr_un, sun_path) + strlen(serv_addr.sun_path);//求取文件名字的实际长度unlink(SERV_FILE);//如果该文件socket存在则先删除bind(lfd, (struct sockaddr*)&serv_addr, len);//绑定给socketlisten(lfd, 20);struct sockaddr_un client_addr;//给accept函数使用,用来绑定客户端的信息    char buf[1024] = {0};    printf("Accept....\n");while(1){int client_len = sizeof(client_addr);//作为accept函数的第三个参数//参数2是客户端本地套接字信息,但是和服务器的一样,参数三获取到的是client_addr.sun_path的实际长度int cfd = accept(lfd, (struct sockaddr*)&client_addr, (socklen_t*)&client_len);len -= offsetof(struct sockaddr_un, sun_path);/* 得到文件名的长度 */client_addr.sun_path[len] = '\0';                 /* 确保打印时,没有乱码出现 */printf("client bind filename %s\n", client_addr.sun_path);int size = 0;int i = 0;while((size = read(cfd, buf, 1024)) > 0){for(i = 0;i < size;i ++){buf[i] = toupper(buf[i]);}printf("客户端%s的消息:%s\n", client_addr.sun_path, buf);write(cfd, buf, size);}close(cfd);}close(lfd);    return 0;
}

4.事件异步通信模型

在这个模型中需要使用到libevent库,下载网址:www.libevent.org,下载tar安装包并且上传到linux服务器

1.如图,是上传的安装包

2.使用如下命令进行解包

tar -zxvf libevent-2.1.10-stable.tar.gx

生成如下文件

3.进入该文件

4.执行如下命令

1. ./configure
2. make
3. sudo make install

这样就可以使用libevent库了

使用libevent创建服务器的过程

1.使用socket创建套接字

2.设置端口复用->setsockopt()

3.绑定IP地址->bind

4.设置监听->listen

5.创建地基->event_base_new()函数

作用:event_base_new()函数分配并且返回一个新的具有默认设置的event_base。函数会检测环境变量,返回一个到event_base的指针。

函数原型:

struct  event_base  * event_base_new(void );返回值:如果发生错误,则返回NULL

6.创建服务器socket对应的事件->event_new函数

作用:分配并初始化一个新的event结构体,准备被添加

函数原型:

struct event*event_new(struct event_base*base, evutil_socket_t fd, short whar, event_callback_fn cb, void *arg);参数解释:参数base:event_base_new的返回值参数 fd:绑定到event上的 文件描述符参数what:对应的事件(r w e)EV_READ    一次 读事件EV_WRTIE    一次 写事件EV_PERSIST    持续触发,结合 event_base_dispatch函数使用,生效参数cb:一旦事件满足监听条件,触发该事件的处理函数,就是回调函数参数arg:回调的函数的参数返回值:成功创建的 event回调函数的规范书写:
typedef void(*event_callback_fn)(evutil_socket_t fd, short, void*)

7.把服务器socket对应的事件放上event_base地基上->event_add函数

作用:该函数将event_new创建出来的事件挂上地基,实现监听

函数原型:

int event_add(struct event* ev, const struct timeval *tv)参数解释:参数ev:event_new的返回值参数tv:表示超时时间NULL:表示没有超时,一直等待,直到有事件触发,才会调用回调函数非0值:如果超过指定的时间,即使没有事件触发也会调用回调函数    

8.进入时间循环->event_base_dispatch

作用:事件调度循环,此循环将运行event,直到不再有挂起或活动的事件,或者直到有东西调用event_base_loopbreak()或event_ base_loopexit()

函数原型:

int event_base_dispatch(struct event_base *);参数:event_base_new创建的返回值事件处理调用函数:
int event_base_loop(struct event_base *, int);作用:等待事件变为活动状态,然后运行它们的回调。这是比event_base_dispatch更灵活版本。
默认情况下,此循环将运行事件库,直到不再存在挂起或活动事件,
或者直到调用event_base_loopbreak()或event_ base_loopexit()参数解释:参数1:event_base_new的返回值参数2:可以设置重写函数的参数返回值:如果成功,则返回0;如果发生错误,则返回1;如果由于没有挂起或活动的事件而退出,则返回2

9.释放资源->event_base_free函数和event_free函数

作用:event_base_free释放地基的资源,event_free函数释放事件资源

使用libevent实现的服务器代码
#include
#include
#include
#include
#include
#includestruct event* connev;//回调函数的原型:typedef void (*event_callback_fn)(evutil_socket_t fd, short events, void* arg)
//读数据回调函数
void readcb(evutil_socket_t fd, short events, void* arg)
{int n ;char buf[1024];memset(buf, 0x00, sizeof(buf));n = read(fd, buf, sizeof(buf));printf("客户端发来消息:%s\n", buf);if(n <= 0){close(fd);//将对应的客户端事件从地基上删除event_del(connev);}elsewrite(fd, buf, n);
}//连接回调函数
void conncb(evutil_socket_t fd, short events, void* arg)
{struct event_base* base = (struct event_base*)arg;//接受客户端连接int cfd = accept(fd, NULL, NULL);if(cfd > 0){    //创建客户端对应的事件并设置回调函数readcb//参数3是事件信号,参数4是回调函数connev = event_new(base, cfd, EV_READ|EV_PERSIST, readcb, NULL);if(connev == NULL){//退出循环event_base_loopexit(base, NULL);printf("connev error\n");exit(1);}//将客户端对应的事件放到地基上event_add(connev, NULL);}
}int main()
{//创建socketint lfd = socket(AF_INET, SOCK_STREAM, 0);//设置端口号复用int opt = 1;setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));//创建socket信息struct sockaddr_in serv_addr;serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(8888);serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));//监听listen(lfd, 128);//创建地基struct event_base* base = event_base_new();if(base == NULL){printf("event_base create error\n");return -1;}//创建监听文件描述符对应的事件struct event* ev = event_new(base, lfd, EV_READ|EV_PERSIST, conncb, base);if(ev == NULL){printf("event_new error\n");return -1;}//将新的事件放到base地基上event_add(ev, NULL);//进入事件循环等待event_base_dispatch(base);//释放资源event_base_free(base);//释放地基资源event_free(ev);//释放事件资源close(lfd);return 0;
}

相关内容

热门资讯

爱国活动主持词 爱国活动主持词范文  主持人在台上表演的灵魂就表现在主持词中。我们眼下的社会,主持词在各种活动中起到...
“我的祖国”演讲比赛主持词 “我的祖国”演讲比赛主持词  主持词要注意活动对象,针对活动对象写相应的主持词。在当下这个社会中,越...
舞蹈节目主持词串词 舞蹈节目主持词串词范文(精选8篇)  主持词需要富有情感,充满热情,才能有效地吸引到观众。在当下的社...
我是歌手歌唱比赛主持词 我是歌手歌唱比赛主持词  小歌手主持词篇一  A:尊敬的各位领导  B:敬爱的老师,亲爱的同学们  ...
中秋节联欢晚会主持词 中秋节联欢晚会主持词(精选11篇)  主持词要注意活动对象,针对活动对象写相应的主持词。我们眼下的社...
初中新生开学欢迎词 初中新生开学欢迎词2017各位初一年全体同学石狮二中敞开胸怀迎接你们,真诚地欢迎你们加入这个大家庭,...
品鉴会主持词 品鉴会主持词  借鉴诗词和散文诗是主持词的一种写作手法。随着中国在不断地进步,各种集会的节目都通过主...
新年半台词 新年三句半台词  三句半是一种中国民间群众传统曲艺表演形式。每段内容有三长句一半句。一般由4人演出,...
消夏文艺晚会的主持词 消夏文艺晚会的主持词(精选11篇)  主持词是主持人在节目进行过程中用于串联节目的串联词。在各种集会...
英雄联盟经典台词 英雄联盟经典台词  英雄联盟经典台词  1、正义,要么靠法律,要么靠武力!  2、你迷失在黑暗之中,...
小学元旦节的主持词 小学元旦节的主持词(精选16篇)  主持词是主持人在台上表演的灵魂之所在。在当今不断发展的世界,各种...
婚纱走秀主持词 婚纱走秀主持词三篇  篇一:婚纱走秀演出主持词  当您披上洁白的婚纱,点亮您一生中最美丽的日子,您是...
医者仁心台词 医者仁心台词大全  1. 钟立行对丁祖望:我们都在努力做一个能够被人怀念的人。  2.罗雪樱旁白:从...
《美丽人生》的经典台词 《美丽人生》的经典台词  意大利电影《美丽人生》,由罗伯托贝尼尼自编自演,讲述了意大利一对犹太父子被...
二年级主持词 二年级主持词  主持词分为会议主持词、晚会主持词、活动主持词、婚庆主持词等。在一步步向前发展的社会中...
年会的主持词 年会的主持词范文(通用5篇)  根据活动对象的不同,需要设置不同的主持词。时代不断在进步,主持词是活...
姨妈的后现代生活经典台词分享 姨妈的后现代生活经典台词分享  吉日良辰当欢笑,为什么鲛珠化泪抛?此时却又明白了,世上何尝尽富豪。也...
学校语文教研活动主持词 学校语文教研活动主持词  借鉴诗词和散文诗是主持词的一种写作手法。在一步步向前发展的社会中,很多晚会...
六一庆祝大会主持词 六一庆祝大会主持词  六一就是我们的节日,六一就是一个欢乐的日子,下面小编整理的六一庆祝大会主持词,...
婚礼感谢词 婚礼感谢词(15篇)婚礼感谢词1各位来宾,各位亲友:  大家晚上好!  今日是我女儿XXX和女婿XX...