在写项目的时候,我们发现,tcp和udp的网络通信模型有很多的相似之处,因此我们可以考虑将其封装成一个函数,集成相关的函数,方便我们后面项目的使用。
#ifndef NETWORK_H
#define NETWORK_H
#include
#include
#include
#include
#include
#include
#include
#include
typedef struct NetWork
{
int type; //通信协议的类型 SOCK_STREAM SOCK_DGRAM
int sock_fd; //socket描述符
struct sockaddr_in addr; //通信地址
socklen_t addrlen; //结构体字节数
bool is_srv; //是否是服务端
}NetWork;
typedef struct sockaddr* SP;
//分配内存,创建socket,准备地址,绑定,监听,连接
NetWork* init_nw(int type,const char* ip,short port,bool is_srv);
//等待连接,只有tcp服务端可以用,SOCK_STREAM
NetWork* accept_nw(NetWork* nw);
//具备send和sendto的发送函数
int send_nw(NetWork* nw,const void* buf,size_t len);
//具备recv和recvform的接收函数
int recv_nw(NetWork* nw,void* buf,size_t len);
//关闭socket并且释放内存
void close_nw(NetWork* nw);
#endif
/**************************************************
> 作 者:杭电羊皮卷
> 文件名称: network.c
> 联系方式:2997675141@qq.com
> 创建日期:2022年09月07日
> 描 述:网络通信的封装tcp和udp
**************************************************/
#include "network.h"
#define handle_error(msg)\
do{perror(msg);free(nw);return NULL;}while(0)
//分配内存,创建socket,准备地址,绑定,监听,连接
NetWork* init_nw(int type,const char* ip,short port,bool is_srv)
{
NetWork* nw = (NetWork* )malloc(sizeof(NetWork));
nw->type = type;
nw->is_srv = is_srv;
nw->addrlen = sizeof(struct sockaddr_in);
//创建socket
nw->sock_fd = socket(AF_INET,type,0);
if(nw->sock_fd < 0)
{
handle_error("socket");
}
//准备通信地址
nw->addr.sin_family = AF_INET;
nw->addr.sin_port = htons(port);
nw->addr.sin_addr.s_addr = inet_addr(ip);
//只有服务器才绑定
if(is_srv)
{
if(0 > bind(nw->sock_fd,(SP)&(nw->addr),nw->addrlen))
{
handle_error("bind");
}
}
//监听 服务器tcp才监听
if(nw->type == SOCK_STREAM && nw->is_srv)
{
if(0 > listen(nw->sock_fd,20))
{
handle_error("listen");
}
}
//连接,只有tcp的客户端才连接
if(nw->is_srv == 0 && type == SOCK_STREAM)
{
int ret = connect(nw->sock_fd,(SP)&nw->addr,nw->addrlen);
if(ret < 0)
{
handle_error("connect");
}
}
return nw;
}
//等待连接,只有tcp服务端可以用,SOCK_STREAM
NetWork* accept_nw(NetWork* nw)
{
if(nw->type == SOCK_STREAM && nw->is_srv)
{
NetWork* new = (NetWork* )malloc(sizeof(NetWork));
new->type = nw->type;
new->is_srv = true;
new->addr = nw->addr;
new->addrlen = nw->addrlen;
new->sock_fd = accept(nw->sock_fd,(SP)&nw->addr,&(nw->addrlen));
if(new->sock_fd < 0)
{
//handle_error("accept");
perror("accept");
free(new);
return NULL;
}
return new;
}
return NULL;
}
//具备send和sendto的发送函数
int send_nw(NetWork* nw,const void* buf,size_t len)
{
int ret;
if(nw->type == SOCK_STREAM)
{
//tcp的发送
ret = send(nw->sock_fd,buf,len,0);
}
else
{
//udp的发送
ret = sendto(nw->sock_fd,buf,len,0,(SP)&(nw->addr),nw->addrlen);
}
return ret;
}
//具备recv和recvform的接收函数
int recv_nw(NetWork* nw,void* buf,size_t len)
{
int ret;
if(nw->type == SOCK_STREAM)
{
//tcp
ret = recv(nw->sock_fd,buf,len,0);
}
else
{
ret = recvfrom(nw->sock_fd,buf,len,0,(SP)&(nw->addr),&(nw->addrlen));
}
return ret;
}
//关闭socket并且释放内存
void close_nw(NetWork* nw)
{
close(nw->sock_fd);
free(nw);
}
首先我们考虑在用户主目录中创建两个目录,一个用于存放自己制作的动态库,一个用来存放在自己的头文件,所以在命令行中输入以下两个命令。
mkdir ~/lib
mkdir ~/include
其中lib用来存放动态库文件(.so文件),include存放头文件(.h文件)
然后添加这两个文件加进环境变量中
输入vim ~/.bashrc
,这个文件是系统的配置文件,切记不要去修改,按下大写的G直接跳转到文末,然后按i进入插入模式,输入以下几行命令
export LIBRARY_PATH=$LIBRARY_PATH:/home/ubuntu/lib //添加静态库查找路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ubuntu/lib //添加动态库加载路径
export C_INCLUDE_PATH=$C_INCLUDE_PATH:/home/ubuntu/include //添加头文件查找路径
然后ctrl+c
退出编辑模式,大写的两个Z退出文件
输入命令source ~/.bashrc
该命令可以使系统配置文件立即生效
一切准备工作就绪,接下来制作动态库,将工作目录调整到network.c
和network.h
所在路径
先后输入命令
gcc -c -fpic network.c
gcc -shared network.o -o libnw.so
然后用ls -l
命令查看可以看到多了一个可执行文件
将libnw.so
文件复制到~/lib
文件
将network.h
文件复制到~/include
文件
cp libnw.so ~/lib
cp network.h ~/include
然后就可以使用该库文件啦,使用方式和数学库一样的,在进行gcc编译的时候需要加载库文件
如:gcc main.c -lnw
就可以正常编译了