近期由于项目需要, 在 linux 下写了一个服务器与客户端之间进行文件的传输, 可以同时监听多个客户端, 与多个客户端进行传输。基本需求如下 :
1、服务端监听客户端, 当监听到客户端连接时,创建一个线程去完成后续处理工作从而实现同时连接多个客户端。
2、命令与数据分开传递,其中命令采用JSON格式。客户端可以通过命令查询指定目录的文件链表,可以指定需要传 输的文件, 当收到的是图片文件时,通过界面显示。
由于项目的特殊性, 这里只能列出部分功能
传输中定义的 Json 的格式如下 :
/* ----------------- 下发的消息 ----------------- */
获取文件链表
{
"MsgType": "Cmd",
"MsgID" : 0x0D,
}
同步指定文件
{
"MsgType" : "Cmd",
"MsgID" : 0x0E,
"FileName" : "/tmp/SD0/DCIM/140101100/001836AB.MP4"
}
/* ----------------- 接收的消息 ----------------- */
连接成功
{
"MsgType" : "ConnectInfo"
"CmdInfoArray" : [
{
"MsgValue" : "Success"
}
]
}
即将同步的文件信息
{
"MsgType" : "FileInfo",
"CmdInfoArray" : [
{
"FileName" : "/tmp/SD0/DCIM/140101100/00013100.JPG",
"FileSize" : 1457969
}
]
}
获取文件列表
{
"MsgType" : "FileListInfo"
"CmdInfoArray" : [
{
"FileName" : "/tmp/SD0/DCIM/140101100/00013100.JPG",
"FileSize" : 1457969
},
{
"FileName" : "/tmp/SD0/DCIM/140101100/00025500.JPG",
"FileSize" : 1468719
},
{
"FileName" : "/tmp/SD0/DCIM/140101100/00025501.JPG",
"FileSize" : 1455959
},
{
"FileName" : "/tmp/SD0/DCIM/140101100/00025600.JPG",
"FileSize" : 1484678
},
{
"FileName" : "/tmp/SD0/DCIM/140101100/000308AA.MP4",
"FileSize" : 41943040
},
{
"FileName" : "/tmp/SD0/DCIM/140101100/000308AB.MP4",
"FileSize" : 20971520
}
]
}
linux Server 核心代码 :
代码可以在 linux 下运行, 也可以在 ARM-Linux 下运行, 如要在 ARM-Linux 下运行, 使用修叉编译器即可
/* 创建指定端口的 socket */
int create_socket(const int portNum, fd_set *set_fd)
{
int ret;
int server_fd = -1;
struct sockaddr_in server_addr;
/* ---------- 创建 IPv4 的流式套接字描述符 ---------- */
if (-1 == (server_fd = socket(AF_INET, SOCK_STREAM, 0)))
{
perror("Socket Error\r\n");
return -1;
}
int yes = 1;
/* -------------- 解决 Addr Areay used -------------- */
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)))
{
perror("setsockopt failed");
return -1;
}
/* ------------------ 设置非阻塞 ------------------ */
set_sock_NonBlock(server_fd);
/* ---------- 填充服务器端 sockaddr 地址结构 ---------- */
bzero(&server_addr, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(portNum);
ret = bind(server_fd,(struct sockaddr *)(&server_addr), sizeof(struct sockaddr));
if (-1 == ret)
{
perror("Bind error\r\n");
return -1;
}
if (-1 == listen(server_fd, 4))
{
perror("Listen error\r\n");
return -1;
}
FD_ZERO(set_fd);
FD_SET(server_fd, set_fd);
return server_fd;
}
/* main 函数 */
int main(int argc, char *argv[])
{
int cmd_server_fd = -1;
int cmd_client_fd = -1;
int dat_server_fd = -1;
int dat_client_fd = -1;
fd_set cmd_set_fd;
fd_set dat_set_fd;
struct sockaddr_in cmd_client_addr;
struct sockaddr_in dat_client_addr;
socklen_t addr_len = (socklen_t)sizeof(struct sockaddr_in);
/*---------------- 创建服务端 Socket ----------------*/
cmd_server_fd = create_socket(CmdPortNum, &cmd_set_fd);
dat_server_fd = create_socket(DatPortNum, &dat_set_fd);
while (1)
{
if ( select(cmd_server_fd + 1, &cmd_set_fd, NULL, NULL,
(struct timeval *)0) > 0 )
{
if (FD_ISSET(cmd_server_fd, &cmd_set_fd) > 0)
{
cmd_client_fd = accept(cmd_server_fd,
(struct sockaddr *)(&cmd_client_addr),
&addr_len);
if ( cmd_client_fd == -1 )
{
perror("Cmd Accept error\r\n");
}
else
{
printf("\r\nCmd Socket Connect from %s\r\n",
inet_ntoa(cmd_client_addr.sin_addr));
pthread_t cmd_thread_pid;
/* 创建新的命令线程 */
pthread_create(&cmd_thread_pid, NULL, (void *)cmd_deal_thread,
&cmd_client_fd);
}
}
}
if ( select(dat_server_fd + 1, &dat_set_fd, NULL, NULL,
(struct timeval *)0) > 0 )
{
if ( FD_ISSET(dat_server_fd, &dat_set_fd) > 0 )
{
dat_client_fd = accept(dat_server_fd,
(struct sockaddr *)(&dat_client_addr), &addr_len);
if (-1 == dat_client_fd)
{
perror("Dat Accept error\r\n");
}
else
{
printf("Data Socket Connect from %s\r\n",
inet_ntoa(dat_client_addr.sin_addr));
pthread_t dat_thread_pid;
/* 创建新的数据线程 */
pthread_create(&dat_thread_pid, NULL,
(void *)data_trans_thread, &dat_client_fd);
}
}
}
}
exit(0);
}
客户端用 Qt 写的, 代码在 Windows 下和 Ubuntu 下均可以使用,
Ubuntu 下的效果 :
windows 下的效果
源代码 :
http://download.youkuaiyun.com/detail/zhou0842/9672416