弄了这么长时间,终于把TCP服务器做好了。现在把它总结一下。
一、服务器框架
看了一些文章,有很多种TCP服务器,其中并发服务器和多路复用I/O服务器我觉得可用。我们以前的系统用的是并发服务器,它的思想是每一个客户机的请求并不由服务器直接处理,而是服务器创建一个子进程来处理。但是它为了响应客户机的请求,服务器要创建子进程来处理。而创建子进程是一种非常消耗资源的操作,以这弃之不用了。我选择了多路复用I/O服务器模型,这里面主要用到了一个函数fdSelect()。它可以使进程阻塞直至有数据可读入、可写、或超时。
框架如下:
int fd, max_fd, tmp_fd;
int fd_array[FD_NUM];
int i, rc;
buffer buf[BUF_SIZE];
fd_set read_fd, all_fd;
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_port = htons(PORT);
if((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
//error
}
if(bind(fd, (struct sockaddr*)&local, sizeof(local)) == -1)
{
//error
}
if(listen(fd, MAX_CONNECTION) == -1)
{
//error
}
memset(fd_array, -1, FD_NUM);
fd_array[0] = fd;
FD_ZERO(&all_fd);
FD_SET(fd, &all_fd);
max_fd = fd;
while(1)
{
read_fd = all_fd;
if(select(max_fd + 1, &read_fd, NULL, NULL, NULL) == -1)
{
continue;
}
if(FD_ISSET(fd, &readfd))
{
if((tmp_fd = accept(fd, NULL, NULL)) == -1)
{
continue;
}
else
{
for(i = 0; i < FD_NUM; i++)
{
if(fd_array[i] == -1)
{
fd_array[i] = tmp_fd;
}
}
FD_SET(tmp_fd, &allfd);
if(tmp_fd > max_fd)
{
max_fd = tmp_fd;
}
}
continue;
}
for(i = 0; i < FD_NUM; i++)
{
if(FD_ISSET(fd_array[i]), &read_fd)
{
rc = recv(fd_array[i], buf, BUF_SIZE, 0);
if(rc <= 0)
{
//error
}
else
{
buf[rc] = '\0';
//your operation
}
break;
}
}
}
我的图像发送用了另一个任务来完成,在这个任务中可以分别实现TCP和UDP传送。因为TCP传送时要用到服务器连接产生的SOCKET,而UDP要用到连接时获得的客户端地址,所以把这些信息都设成了全局变量。
二、调试时出的几个问题
1、连接数的限定
原来想的是如何到了限定的数目后怎样不准它建立连接,程序写得很麻烦。后来才想到,可以先建立连接,再进行判断,如果超出,则断开即可。这样的做会出现的问题是在客户端一定要对此做处理,否则板子这儿已经断开了连接,但客户端还显示了连接上。
2、SOCKET在两个任务之间的传递
TASKSEND任务里,当用TCP传送图像时,需要tsktcpserver任务里生成的连接socket,我原以为把这个做成全局变量后就可以传递了。但实际不行。查了NDK文档后才知道,每个任务都有自己的文件描述符表,所以必须加上描述符共享命令,对描述符进行拷贝。
3、无法支持UDP传送
TCP传送成功后,不知为什么,UDP的SENDTO一直返回为45号错误。我查了好长时间,最后问题还是出在那个描述符共享命令了。TCP需要描述符共享,但UDP不需要。
4、全局变量的使用
在调试时,发现TCP发送图像时,不能对多个已经连接的用户进行传送,仔细检查后,发现是全局变量的问题,这个值在另一个线程中被修改,所以造成错误。去掉这个全局变量,问题解决了。
基本上就遇到这3个问题。