linux嵌入式开发中,网络编程是绕不开的,现在设备的IP地址一般都是通过dhcp自动获取的,没有一个固定不变的IP,在和服务器进行通信时就不能才用既是客户端又是服务器的写法。而应采用常连接的方式,即服务器不需特意知道客户端的ip,只需知道一个TCP/IP的连接即可。本文来讲解一下嵌入式设备中的客户端网络编程。
一 、网络编程中的常用函数介绍
经常使用的函数主要有:
(1)getsockopt,用于获取网络套接字选项。
(2)setsockopt,用于设置网络套接字选项。
(3)connect,连接服务器。
二 、 检测网络连接状况
int SocketConnected(int sock)
{
if(sock <= 0)
return 0;
struct tcp_info info;
int len = sizeof(info);
getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);
if((info.tcpi_state == TCP_ESTABLISHED))
{
printf("socket connected\n");
return 1;
}
else
{
printf("socket disconnected\n");
return 0;
}
}
三 、 常连接服务器
int Connect_to_Remote_Tcp_Server(void)
{
struct sockaddr_in sk_address;
int sockaddr_len;
int remote_tcp_client_fd =0;
int remote_tcp_client_res=0;
int nNetTimeout=1000;//2秒
int keepAlive = 1; // 开启keepalive属性
int keepIdle = 60;
int keepInterval = 60;
int keepCount = 3;
long lip;
memcpy(&lip,devpara.sysdevip.serverip,4);
memset(&sk_address,0,sizeof(sk_address));
sk_address.sin_family = AF_INET;
sk_address.sin_addr.s_addr = lip;
sk_address.sin_port = htons(CMDTCPSERVEPORT);
sockaddr_len = sizeof(sk_address);
remote_tcp_client_fd = socket(AF_INET, SOCK_STREAM, 0);
if (remote_tcp_client_fd == -1)
{
perror ("socket call failed");
exit (errno);
}
net_socket_setblock(remote_tcp_client_fd, 0);
remote_tcp_client_res = connect(remote_tcp_client_fd, (struct sockaddr *)&sk_address,sockaddr_len);
if(remote_tcp_client_res!=0)
{
if(errno != EINPROGRESS) {
printf("socket connected failed\n");
close(remote_tcp_client_fd);
return -1;
}
fd_set set;
FD_ZERO(&set);
FD_SET(remote_tcp_client_fd, &set);
struct timeval connect_tmout;
connect_tmout.tv_sec=1;
connect_tmout.tv_usec=0;
if( select(remote_tcp_client_fd+1, NULL, &set, NULL, &connect_tmout) <= 0){
printf("socket connected timeout\n");
close(remote_tcp_client_fd);
return -1;
}
}
net_socket_setblock(remote_tcp_client_fd, 1);
setsockopt(remote_tcp_client_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
setsockopt(remote_tcp_client_fd, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
setsockopt(remote_tcp_client_fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
setsockopt(remote_tcp_client_fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));
//发送时限
setsockopt(remote_tcp_client_fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&nNetTimeout,sizeof(int));
//接收时限
setsockopt(remote_tcp_client_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int));
return remote_tcp_client_fd;
}
设置阻塞方式或是非阻塞方式
void net_socket_setblock(int s, int is_block)
{
int opts=fcntl(s, F_GETFL);
if(opts < 0) return;
if(is_block)
opts = opts & ~O_NONBLOCK;
else
opts = opts | O_NONBLOCK;
fcntl(s, F_SETFL, opts);
}
四 、 实例
void main()
{
int fd;
pthread_t skrec_thread;
if(SocketConnected(fd) == 0)
{
fd = Connect_to_Remote_Tcp_Server();
if(fd >= 0)
{
pthread_create(&skrec_thread,NULL,Socket_Data_Proc,fd);
pthread_detach(skrec_thread);
}
}
}
其中Socket_Data_Proc为数据接收函数。