渣渣嵌入式的程序狗学网络
1)理论知识
有个小心得,当FD_ISSET(fd)为真时,处理完fd后,记得FD_CLR(fd, pset)。原因是当有多条session链表时,可能需要将一个session从一条session链表移到另外一条session链表。二每条session链表的处理是有先后顺序的,防止一个session被处理2次,第二次会阻塞。
注意了select的超时时间和set每次都需要重新设置,如果超时后,tv超时时间变成了0,不重新设置的话,超时时间为0,就相当于轮训了。
2)实际操作,做项目,自己写测试程序
今天我写了一个select的测试程序,放出来。希望还没开始学习网络编程的,自己也写一个测试程序,写得多了,就慢慢的入门了。
理论知识:epoll最好,poll次之,select最垃圾。学习指定从最垃圾的开始。可以看看这些博客来补充一下自己的理论的知识https://blog.csdn.net/fdgyfghh/article/details/83926265
实际操作:
server端示例:
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
struct Session{
int fd;
struct sockaddr_in addr;
char data[1024];
Session* next;
};
struct SessionHead{
Session head;
int fd;
int linknum;
int max_fd;
int max_link;
};
static void ap_server_exit(void* ptr)
{
printf ("ap_server_exit\n");
SessionHead* stab = (SessionHead*)ptr;
if (!stab)
{
return;
}
int i = 0;
Session* tmp = stab->head.next;
Session* priv = &stab->head;
Session* next;
for ( ; tmp != NULL; tmp = next)
{
next = tmp->next;
if (tmp->fd > 0)
{
close(tmp->fd);
i++;
}
tmp->fd = 0;
tmp->next = NULL;
free (tmp);
}
stab->head.next = NULL;
if (stab->fd > 0)
{
close (stab->fd);
}
stab->fd = 0;
stab->max_fd = 0;
stab->linknum = 0;
stab->max_link = 0;
printf ("close listen sockect, close the all(%d) client\n", i);
return;
}
static void ap_init_seleteset(fd_set* set, SessionHead* stab)
{
if(!stab|| !set)
{
return;
}
FD_ZERO(set);
if (stab->fd > 0)
{
printf ("add listen sockect to set, fd=%d\n", stab->fd);
FD_SET(stab->fd, set);
}
Session* tmp = stab->head.next;
int max_fd = stab->fd;
for ( ; tmp != NULL; tmp = tmp->next)
{
if (tmp->fd > 0)
{
printf ("add accept sockect to set, fd=%d\n", tmp->fd);
FD_SET(tmp->fd, set);
}
if (tmp->fd > max_fd)
{
max_fd = tmp->fd;
}
}
printf ("max fd=%d\n", max_fd);
stab->max_fd = max_fd;
return;
}
void* ap_server_thread(void* argc)
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd <= 0)
{
printf ("socket open err\n");
return NULL;
}
sockaddr_in ser_addr;
memset (&ser_addr, 0, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(30084);
ser_addr.sin_addr.s_addr = INADDR_ANY;
int on = 1;
int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR , &on, sizeof(on));
if (ret < 0)
{
perror("setsockopt failed");
return NULL;
}
ret = bind(fd, (sockaddr*)&ser_addr, sizeof(ser_addr));
if (ret != 0)
{
printf ("bind err\n");
return NULL;
}
ret = listen(fd, 20);
if (ret != 0)
{
printf ("listen err\n");
return NULL;
}
SessionHead stab;
stab.head.next = NULL;
stab.max_fd = fd;
stab.max_link = 10;
stab.fd = fd;
Session* head = &stab.head;
pthread_cleanup_push(ap_server_exit, &stab);
fd_set rset;
struct timeval tv;
FD_ZERO(&rset);
FD_SET(fd, &rset);
while(1)
{
pthread_testcancel();
ap_init_seleteset(&rset, &stab);
tv.tv_sec = 20;
tv.tv_usec = 0;
ret = select(stab.max_fd+1, &rset, NULL, NULL, &tv);
if (ret < 0)
{
perror("select error:");
break;
}
else if (ret == 0)
{
continue;
}
else
{
printf ("some socket read ready, num=%d\n", ret);
/* session message */
Session* tmp = head->next;
Session* priv = head; /* have entity head is good */
for (; tmp != NULL; tmp=tmp->next)
{
if(!FD_ISSET(tmp->fd, &rset))
{
continue;
}
memset (tmp->data, 0, sizeof(tmp->data));
int num = recv(tmp->fd, tmp->data, sizeof(tmp->data) - 1, 0);
if (num <= 0)
{
printf ("a client leave\n");
close (tmp->fd);
tmp->fd = 0;
priv->next = tmp->next;
tmp->next = NULL;
free (tmp);
}
else
{
printf ("recv data len=%d\n", num);
printf ("recv data:%s\n", tmp->data);
char rsp[64];
memset (rsp, 0 , sizeof(rsp));
snprintf (rsp, sizeof(rsp), "i am server,i recv data len=%d", num);
num = send (tmp->fd, rsp, strlen(rsp), 0);
printf("%s, and send len=%d\n", rsp, num);
if (strstr (tmp->data, "finish"))
{
char* finish = "finish";
num = send (tmp->fd, finish, strlen(finish), 0);
printf ("recv a finish, send a finish.num=%d\n", num);
}
}
priv = tmp;
}
if (FD_ISSET(fd, &rset))
{
printf ("a new connect occur\n");
Session* ses = (Session*)calloc (1, sizeof(Session));
socklen_t len = sizeof(ses->addr);
ses->fd = accept(fd, (sockaddr*)&ses->addr, &len);
if (ses->fd <= 0)
{
printf ("accept error");
}
else
{
printf ("accept successful, fd=%d ip=%s, port=%d\n",
ses->fd, inet_ntoa(ses->addr.sin_addr) , ntohs(ses->addr.sin_port));
int i = 0;
Session* tmp = head;
Session* tail = head;
for ( ; tmp != NULL; tmp= tmp->next)
{
tail = tmp;
i++;
}
if ((i-1) > stab.max_link)
{
printf ("max session :10, i will close the new client\n");
close(ses->fd);
ses->fd = 0;
free (ses);
ses = NULL;
}
else
{
int enable = 1;
setsockopt(ses->fd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));
tail->next = ses;
printf ("add session successful\n");
}
}
}
}
}
pthread_cleanup_pop(1);
return NULL;
}
int main(int argc, char* argv[])
{
pthread_t tid ;
int ret = pthread_create(&tid, NULL, ap_server_thread, NULL);
if (ret != 0)
{
printf("pthread create failed\n");
return 0;
}
printf ("--------------sleep 60s------\n");
sleep (60);
pthread_cancel(tid);
pthread_join(tid, NULL);
return 0;
}
client的示例:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
int main(int argc, char* argv[])
{
if (argc != 4)
{
printf ("usage:%s ip port data\n", argv[0]);
printf ("for example: %s 192.168.99.144 30084 helloword\n", argv[0]);
return -1;
}
char* ip = argv[1];
int port = atoi(argv[2]);
char* data = argv[3];
printf ("ip:%s port:%d data:%s\n", ip, port, data);
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd <= 0)
{
printf ("socket error\n");
return -1;
}
struct sockaddr_in s_addr;
memset(&s_addr, 0, sizeof(s_addr));
s_addr.sin_family= AF_INET;
s_addr.sin_port = htons(port);
s_addr.sin_addr.s_addr = inet_addr(ip);
int ret = connect (fd, (sockaddr*)&s_addr, sizeof(s_addr));
if (ret != 0 )
{
perror("connect error");
return -1;
}
//int enable = 1;
//ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable));
//printf ("setsockopt tcp nodelay %d\n",ret);
int i;
int num = 0;
int sum = 0;
for (i = 0; i < 5; i++)
{
num = send(fd, data, strlen(data), 0);
if (num > 0)
{
sum += num;
}
printf ("send len=%d and real=%d total=%d\n",
strlen(data), num, sum);
}
char* finish = "finish";
num = send(fd, finish, strlen(finish), 0);
printf ("send a finish to server, num=%d\n", num);
int recvnum = 0;
char buf [1024];
while (1)
{
recvnum = recv (fd, buf, sizeof(buf)-1, 0);
buf[recvnum] = '\0';
printf ("recv num recvnum=%d\n", recvnum);
printf ("recv data=%s\n", buf);
if (recvnum <= 0)
{
printf ("the connect is unreachable\n");
break;
}
if (strstr(buf, "finish"))
{
printf ("recive a finish, exit\n");
break;
}
}
close (fd);
return 0;
}
对于像我这样的渣比们,一定要学网络编程。