千呼万唤始出来,我终于在电脑里面找到一个去年的 openwrt 的socket多进程服务端代码,虽然是初期的代码,当时麻雀虽小,五脏俱全啊,功能大致还有,想想当时学习 c 语言写代码的时候真是感慨啊,可是写了那么多代码,没从公司复制过来,有点失策。
下面简单介绍下这个 openwrt 上的 socket 多进程服务端吧。
看看代码
这个是 c 语言代码,当时写的代码不怎么样,勿喷啊。需要说明的是,这里用到了 Linux 的一些库,直接在 Windows 上面是无法编译的,可以在 Windows上的子系统编译,有需要可以看我之前博客。
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <signal.h>
#include <pthread.h>
#define IP "192.168.121.1"
#define PORT 2050
#define QUEUE 10
#define BUFFER_SIZE 8196
//接收数据
void *recv_data(int conn_fd);
//获取字符串形式文件参数
char* get_args();
int main()
{
printf("This is 17:31");
//手动捕获管道破裂信号,并忽略
signal(SIGPIPE, SIG_IGN);
///定义sockfd
int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);
int conn;
///定义sockaddr_in
struct sockaddr_in server_sockaddr;
server_sockaddr.sin_family = AF_INET;//IPv4
server_sockaddr.sin_port = htons(PORT);
server_sockaddr.sin_addr.s_addr = inet_addr(IP);//需要置顶IP,不然连不上
bzero(&(server_sockaddr.sin_zero),8);//填充结构体剩余部分
///bind,成功返回0,出错返回-1
if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)
{
perror("bind");
exit(1);
}
printf("bind success, start listen!");
///listen,成功返回0,出错返回-1
if(listen(server_sockfd,QUEUE) == -1)
{
perror("listen");
exit(1);
}
while(1)
{
pthread_t thread;
///客户端套接字
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr);
if (-1 == (conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length)))
{
perror("accept");
continue;
}
if (0!= pthread_create(&thread, NULL, recv_data, conn))
{
perror("pthread_create");
break;
}
}
shutdown(server_sockfd,2);
shutdown(conn,2);
return 0;
}
void *recv_data(int conn_fd)
{
int recv_len;
char recv_buf[BUFFER_SIZE];
for(;;)
{
bzero(recv_buf, sizeof(recv_buf));
recv_len = recv(conn_fd,recv_buf,BUFFER_SIZE,0);
if(-1==recv_len)
{
perror("recv");
exit(1);
}
recv_buf[recv_len] = '\0';
if(strncmp(recv_buf,"exit",4)==0)
{
printf("%s\n",recv_buf);
break;
}
printf("Receive data:%s\n", recv_buf);
if(send(conn_fd, get_args(), strlen(get_args()), 0) == -1)
{
perror("send");
exit(1);
}
}
close(conn_fd);
pthread_exit(NULL);
}
char* get_args()
{
//char data[8196];
//char *data = malloc(8196);
char *data = (char*)calloc(BUFFER_SIZE, sizeof(char));//带初始化
char tmp[8] = { 0 };
FILE* pFile = fopen("/etc/config/site-manager", "r");
while ( fread(tmp, sizeof(char), 2, pFile) )
{
strcat(data,tmp);
bzero(tmp,sizeof(tmp));
//memset(tmp,0,sizeof(tmp));
}
fclose(pFile);
return data;
}
这里用到了 pthread 来做多线程,数据是从 /etc/config/ 拿的 uci 参数,后面有时间可以讲讲 uci 参数,这个是可以从网页上修改的,支持双向绑定。
这里使用的是 UDP的服务端,也可以写成 TCP,都差不多,可以自行研究。
int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);
其他额注释都写的很清楚,当时刚毕业态度还是很端正的,值得注意的是有个管道破裂的问题,在 socket 客户端直接挂断 socket 连接,服务端不知情,会导致服务端到客户端连着,客户端到服务端断开的情形,这时候服务端应用一旦发送数据到客户端,就会出现管道破裂的 error,导致服务端程序被关闭,反正就是让你加上下面这句话吧
//手动捕获管道破裂信号,并忽略
signal(SIGPIPE, SIG_IGN);
编写配置文件
因为引入了 pthread 库,我们的配置文件需要做出一定修改。
- 源码目录 Makefile
argserver : argserver.o
$(CC) $(LDFLAGS) argserver.o -o argserver -lpthread
argserver.o : argserver.c
$(CC) $(CFLAGS) -c argserver.c
clean :
rm *.o argserver
这里在编译的时候需要加上 -lpthread,表示增加该库参与编译。
- 外层 Makefile
include $(TOPDIR)/rules.mk
PKG_NAME:=argserver
PKG_RELEASE:=1.0
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
TARGET_LDFLAGS:= -lpthread
include $(INCLUDE_DIR)/package.mk
define Package/argserver
SECTION:=utils
CATEGORY:=Utilities
TITLE:=argserver -- socket server to get arg info
DEPENDS:=+libpthread
endef
define Package/argserver/description
socket server to get arg info .
endef
define Build/Prepare
echo "Here is Package/Prepare"
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./src/* $(PKG_BUILD_DIR)/
endef
define Package/argserver/install
echo "Here is Package/install"
$(INSTALL_DIR) $(1)/bin $(1)/etc/init.d/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/argserver $(1)/bin/
$(INSTALL_BIN) ./auto/argserver $(1)/etc/init.d/
endef
$(eval $(call BuildPackage,argserver))
注意两处修改,下面表示增加依赖库,上面那个是参与编译。
支持 pthread
一般来说,openwrt 应该默认增加了 pthread 库,如果不确定的话,可以在菜单里面找找
make menuconfig
在菜单里面使用 “/”,搜索 pthread 看看有没有被勾选上,勾选上了我们的程序就能正常运行了。如果你用的 openwrt 没法编译固件,就要看你用的固件支不支持 pthread 了,一般来说都有。
测试
网上随便找个 udp 测试工具就可以了 ,像什么 TCPUDPDbg 啊,网上下一个,用起来很简单,我就不写了。也推荐一下一个多功能工具 IPOP,用起来不错,听说是华为开发,搜一下就知道了。
结语
什么自启动功能就看我上一篇博客吧。
end
完美撒花