嵌入式Linux網路程式設計,I/O多路複用,poll()示例,poll()客戶端,poll()伺服器,單鏈表
阿新 • • 發佈:2018-12-14
文章目錄
1,IO複用poll()示例
1.1,poll()—net.h
#ifndef __NET_H__
#define __NET_H__
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/select.h>
#define SERV_IP_ADDR "192.168.31.100"
#define SERV_PORT 5002
#define BACKLOG 5
#define QUIT_STR "quite"
#define SERV_RESP_STR "Server:"
#endif
1.2,poll()—client.c
/* ./client serv_ip serv_port */
#include "net.h"
#include "poll.h"
void usage(char *s)
{
printf("Usage: %s <serv_ip> <serv_port>\n",s);
printf("\tserv_ip: server ip address\n" );
printf("\tserv_port: server port(>5000)\n ");
}
void client_do_poll(int fd);
int main(int argc, const char *argv[])
{
int fd;
short port;
struct sockaddr_in sin;
if(argc != 3)
{
usage((char *)argv[0]);
exit(1);
}
if((port = atoi(argv[2])) < 5000)
{
usage((char *)argv[0]);
exit(1);
}
/* 1 建立socket fd */
if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
perror("socket");
exit(-1);
}
/* 2 連線伺服器 */
/* 2.1 填充struct sockaddr_in結構體變數*/
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);//轉為網路位元組序埠號
if(inet_pton(AF_INET,argv[1],(void *)&sin.sin_addr.s_addr) < 0)
{
perror("inet_pton");
goto _error1;
}
/* 2.2 連線伺服器*/
if(connect(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
{
perror("connect");
goto _error1;
}
printf("client staring ... OK!\n");
client_do_poll(fd);
_error1:
close(fd);
return 0;
}
void client_do_poll(int fd)
{
char buf[BUFSIZ];
int ret = -1;
struct pollfd f[2];//將需要監聽的fd放入陣列中
f[0].fd = fd;//新增連線描述符
f[0].events = POLLIN;
f[1].fd = STDIN_FILENO;//新增標準輸入描述符
f[1].events = POLLIN;
while(1)
{
poll(f,2,-1);
if(f[1].revents & POLLIN)//標準輸入裡面是不是有輸入
{
/* 讀取鍵盤輸入,傳送到網路套接字fd */
bzero(buf,BUFSIZ);
do
{
ret = read(0,buf,BUFSIZ-1);
}while(ret <0 && EINTR == errno);
if(ret < 0)
{
perror("read");
continue ;
}
if(ret == 0)//沒讀到資料
{
continue;
}
if(write(fd,buf,strlen(buf)) < 0)
{
perror("write() to socket");
continue ;
}
if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)//退出在傳送之後
{
printf("client is existing!\n");
break;
}
}
if(f[0].revents & POLLIN)//伺服器傳送過來了資料
{
/* 讀取套接字資料,處理 */
bzero(buf,BUFSIZ);
do
{
ret = read(fd,buf,BUFSIZ-1);
}while(ret <0 && EINTR == errno);
if(ret < 0)
{
perror("read from socket");
continue ;
}
if(ret == 0)//從套接字中讀到的資料個數小於0,說明伺服器關閉
{
break ;
}
printf("server said: %s",buf);
if((strlen(buf) > strlen(SERV_RESP_STR)) && strncasecmp(buf+strlen(SERV_RESP_STR),QUIT_STR,strlen(QUIT_STR)) == 0)
{
printf("sender client is existing!\n");
break;
}
}
}
close(fd);
}
1.3,poll()—sever.c
#include "net.h"
#include "linklist.h"
#include <sys/ioctl.h>
#define MAX_LEN 10 //poll函式掃描的fd陣列的最大空間
/* IO多路複用poll()處理函式 */
void sever_do_poll(int fd);
int main(int argc, const char *argv[])
{
int fd;
struct sockaddr_in sin;//如果是IPV6的程式設計,要使用struct sockddr_in6結構體(詳細情況請參考man 7 ipv6),通常更通用的方法可以通過struct sockaddr_storage來程式設計
/* 1 建立socket fd */
if((fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
perror("socket");
exit(-1);
}
/* 優化 1 允許繫結地址快速重用 */
int b_reuse = 1;
setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&b_reuse,sizeof(int));
/* 2 繫結 */
/* 2.1 填充struct sockaddr_in 結構體變數*/
bzero(&sin,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
#if 1
/* 優化 2 讓伺服器可以繫結在任意的IP上*/
sin.sin_addr.s_addr = htonl(INADDR_ANY);
#else
if(inet_pton(AF_INET,SERV_IP_ADDR,(void *)&sin.sin_addr.s_addr) < 0)
{
perror("inet_pton");
goto _error1;
}
#endif
/* 2.2 繫結*/
if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)))
{
perror("bind");
goto _error1;
}
/* 3 使用listen()把主動套接字變成被動套接字 */
if(listen(fd,BACKLOG) < 0)
{
perror("listen");
goto _error1;
}
sever_do_poll(fd);
_error1:
close(fd);
return 0;
}
void sever_do_poll(int fd)
{
linklist fdlist,sin_list;//建立一個列表,用於檔案描述符及客戶端資訊儲存
fdlist = create_linklist();
datatype sin_data;//每個物件包括客戶端的socket fd,ipv4地址,埠號
sin_data.pfds.fd = fd;
sin_data.pfds.events = POLLIN;
int maxfd = fd;
//struct timeval tout = {5,0};
insert_end_linklist(fdlist,sin_data);//將lsten()處理後的fd加入列表
//show_linklist(fdlist);
int newfd = -1;
int ret = -1;
char buf[BUFSIZ];//BUFSIZ是系統提供的
char resp_buf[BUFSIZ+10];
struct sockaddr_in cin;
socklen_t cin_addr_len = sizeof(cin);
/* 用poll()函式實現I/O多路複用*/
while(1)
{
int i;
struct pollfd refd[MAX_LEN];
for(i=0;i<MAX_LEN;i++)
{
refd[i].fd = -1;
}
if(get_length_linklist(fdlist) >= 1)//將列表中的fd加入讀集合進行處理
{
for(i=0;i<get_length_linklist(fdlist);i++)
{
sin_list = get_list_pos_linklist(fdlist,i);
sin_data = sin_list->data;
refd[i].fd = sin_data.pfds.fd;
refd[i].events = sin_data.pfds.events;
maxfd = i;
//printf("第 %d 個(fd:%d)(ip:%s)(port:%d)\n",i,sin_data.pfds.fd,sin_data.ipv4_addr,sin_data.port);
}
}
else
{
continue ;
}
switch(poll(refd,maxfd+1,-1))
{
case 0:
{
printf("time out!\n");
goto _error1;
}
case -1:
{
perror("select");
goto _error1;
}
default:
{
sin_data.pfds.fd = fd;
sin_list = get_list_locate_linklist(fdlist,sin_data);
sin_data = sin_list->data;
if(refd[0].revents & POLLIN)//有客戶端傳送了連線請求
{
if((newfd = accept(fd,(struct sockaddr *)&cin,&cin_addr_len)) < 0)
{
perror("connect");
goto _error1;
}
/* 將分配成功的套接字newfd設定成非阻塞模式*/
int b_on = 1;
ioctl(newfd, FIONBIO, &b_on);//將分配成功的套接字newfd設定為非阻塞方式
sin_data.pfds.fd = newfd;
sin_data.pfds.events = POLLIN;
if(inet_ntop(AF_INET,&cin.sin_addr.s_addr,sin_data.ipv4_addr,sizeof(sin_data.ipv4_addr)) < 0)
{
perror("inet_ntop");
goto _error2;
}
sin_data.port = ntohs(cin.sin_port);
printf("get a new client->(ip:%s)(port:%d)(fd:%d)\n",sin_data.ipv4_addr,sin_data.port,sin_data.pfds.fd);
insert_end_linklist(fdlist,sin_data);//將建立客戶端連線的fd加入列表
//show_linklist(fdlist);
}
else//有連線好的客戶端傳送了資料
{
for(i=0;i<get_length_linklist(fdlist);i++)//將連結串列中的fd都處理一遍
{
sin_list = get_list_pos_linklist(fdlist,i);
sin_data = sin_list->data;
//printf("reading fd is ->(第 %d 個)(fd:%d)(ip:%s)(port:%d)\n",i,sin_data.pfds.fd,sin_data.ipv4_addr,sin_data.port);
if(sin_data.pfds.fd == fd)//不是建立連線後分配的newfd
continue ;
//puts("########read before");
bzero(buf,BUFSIZ);
do
{
ret = read(sin_data.pfds.fd,buf,BUFSIZ-1);
}while(ret < 0 && errno == EINTR);//阻塞讀寫
if(ret < 0)
{
//perror("read");
continue;
}
if(ret == 0)//對方已關閉
{
printf("client is existing!\n");
delete_locate_linklist(fdlist,sin_data);
continue;
}
printf("client ip(:%s) port(:%d) fd(:%d) receive data: %s",sin_data.ipv4_addr,sin_data.port,sin_data.pfds.fd,buf);
bzero(resp_buf,BUFSIZ+25);
strncpy(resp_buf,SERV_RESP_STR,strlen(SERV_RESP_STR));
strcat(resp_buf,"ip(");
strcat(resp_buf,sin_data.ipv4_addr);
char s_port[10];
strcat(resp_buf," port(");
sprintf(s_port,"%d",sin_data.port);
strcat(resp_buf,s_port);
strcat(resp_buf," data(");
strcat(resp_buf,buf);
do
{
ret = write(sin_data.pfds.fd,resp_buf,strlen(resp_buf));
}while(ret < 0 && EINTR == errno);
if(strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)) == 0)
{
printf("client (fd:%d)(ip:%s)(potr:%d) is existing!\n",sin_data.pfds.fd,sin_data.ipv4_addr,sin_data.port);
delete_locate_linklist(fdlist,sin_data);//將退出的客戶端的fd從列表中刪除
close(sin_data.pfds.fd);
//show_linklist(fdlist);
}
}
}
}
}
}
_error2:
close(newfd);
_error1:
close(fd);
clear_linklist(fdlist);//將連結串列記憶體釋放
}
1.4,poll()—linklist.h
#ifndef __SINGLE_LINKLIST_H__
#define __SINGLE_LINKLIST_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/poll.h>
typedef struct{
struct pollfd pfds;
char ipv4_addr[16];
int port;
}datatype;
typedef struct node{
datatype data;
struct node *next;
}listnode,*linklist;
linklist create_linklist(void);
linklist create_n_linklist(void);
int delete_pos_linklist(linklist L,int pos);
int delete_locate_linklist(linklist L,datatype x);
void clear_linklist(linklist L);
int get_length_linklist(linklist L);
linklist get_list_pos_linklist(linklist L,int pos);
linklist get_list_locate_linklist(linklist L,datatype x);
int insert_head_linklist(linklist L,datatype x);
int insert_n_head_linklist(linklist L);
int insert_end_linklist(linklist L,datatype x);