linux如何实现网络高级编程,嵌入式Linux网络编程之:网络高级编程-嵌入式系统-与非网...
10.3??網絡高級編程
在實際情況中,人們往往遇到多個客戶端連接服務器端的情況。由于之前介紹的如connet()、recv()和send()等都是阻塞性函數,如果資源沒有準備好,則調用該函數的進程將進入睡眠狀態,這樣就無法處理I/O多路復用的情況了。本節給出了兩種解決I/O多路復用的解決方法,這兩個函數都是之前學過的fcntl()和select()(請讀者先復習第6章中的相關內容)。可以看到,由于在Linux中把socket也作為一種特殊文件描述符,這給用戶的處理帶來了很大的方便。
1.fcntl()
函數fcntl()針對socket編程提供了如下的編程特性。
n 非阻塞I/O:可將cmd設置為F_SETFL,將lock設置為O_NONBLOCK。
n 異步I/O:可將cmd設置為F_SETFL,將lock設置為O_ASYNC。
下面是用fcntl()將套接字設置為非阻塞I/O的實例代碼:
/*?net_fcntl.c?*/
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#define?PORT??????????????????1234
#define?MAX_QUE_CONN_NM????????5
#define?BUFFER_SIZE?????????????1024
int?main()
{
struct?sockaddr_in?server_sockaddr,?client_sockaddr;
int?sin_size,?recvbytes,?flags;
int?sockfd,?client_fd;
char?buf[BUFFER_SIZE];
if?((sockfd?=?socket(AF_INET,?SOCK_STREAM,?0))?==?-1)
{
perror("socket");
exit(1);
}
server_sockaddr.sin_family?=?AF_INET;
server_sockaddr.sin_port?=?htons(PORT);
server_sockaddr.sin_addr.s_addr?=?INADDR_ANY;
bzero(&(server_sockaddr.sin_zero),?8);
int?i?=?1;/*?允許重復使用本地地址與套接字進行綁定?*/
setsockopt(sockfd,?SOL_SOCKET,?SO_REUSEADDR,?&i,?sizeof(i));
if?(bind(sockfd,?(struct?sockaddr?*)&server_sockaddr,
sizeof(struct?sockaddr))?==?-1)
{
perror("bind");
exit(1);
}
if(listen(sockfd,MAX_QUE_CONN_NM)?==?-1)
{
perror("listen");
exit(1);
}
printf("Listening....\n");
/*?調用fcntl()函數給套接字設置非阻塞屬性?*/
flags?=?fcntl(sockfd,?F_GETFL);
if?(flags?
{
perror("fcntl");
exit(1);
}
while(1)
{
sin_size?=?sizeof(struct?sockaddr_in);
if?((client_fd?=?accept(sockfd,
(struct?sockaddr*)&client_sockaddr,?&sin_size))?
{
perror("accept");
exit(1);
}
if?((recvbytes?=?recv(client_fd,?buf,?BUFFER_SIZE,?0))?
{
perror("recv");
exit(1);
}
printf("Received?a?message:?%s\n",?buf);
}?/*while*/
close(client_fd);
exit(1);
}
運行該程序,結果如下所示:
$?./net_fcntl
Listening....
accept:?Resource?temporarily?unavailable
可以看到,當accept()的資源不可用(沒有任何未處理的等待連接的請求)時,程序就會自動返回。
2.select()
使用fcntl()函數雖然可以實現非阻塞I/O或信號驅動I/O,但在實際使用時往往會對資源是否準備完畢進行循環測試,這樣就大大增加了不必要的CPU資源的占用。在這里可以使用select()函數來解決這個問題,同時,使用select()函數還可以設置等待的時間,可以說功能更加強大。下面是使用select()函數的服務器端源代碼。客戶端程序基本上與10.2.3小節中的例子相同,僅加入一行sleep()函數,使得客戶端進程等待幾秒鐘才結束。
/*?net_select.c?*/
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#include?
#define?PORT?????????????????4321
#define?MAX_QUE_CONN_NM???????5
#define?MAX_SOCK_FD????????????FD_SETSIZE
#define?BUFFER_SIZE????????????1024
int?main()
{
struct?sockaddr_in?server_sockaddr,?client_sockaddr;
int?sin_size,?count;
fd_set?inset,?tmp_inset;
int?sockfd,?client_fd,?fd;
char?buf[BUFFER_SIZE];
if?((sockfd?=?socket(AF_INET,?SOCK_STREAM,?0))?==?-1)
{
perror("socket");
exit(1);
}
server_sockaddr.sin_family?=?AF_INET;
server_sockaddr.sin_port?=?htons(PORT);
server_sockaddr.sin_addr.s_addr?=?INADDR_ANY;
bzero(&(server_sockaddr.sin_zero),?8);
int?i?=?1;/*?允許重復使用本地地址與套接字進行綁定?*/
setsockopt(sockfd,?SOL_SOCKET,?SO_REUSEADDR,?&i,?sizeof(i));
if?(bind(sockfd,?(struct?sockaddr?*)&server_sockaddr,
sizeof(struct?sockaddr))?==?-1)
{
perror("bind");
exit(1);
}
if(listen(sockfd,?MAX_QUE_CONN_NM)?==?-1)
{
perror("listen");
exit(1);
}
printf("listening....\n");
/*將調用socket()函數的描述符作為文件描述符*/
FD_ZERO(&inset);
FD_SET(sockfd,?&inset);
while(1)
{
tmp_inset?=?inset;
sin_size=sizeof(struct?sockaddr_in);
memset(buf,?0,?sizeof(buf));
/*調用select()函數*/
if?(!(select(MAX_SOCK_FD,?&tmp_inset,?NULL,?NULL,?NULL)?>?0))
{
perror("select");
}
for?(fd?=?0;?fd?
{
if?(FD_ISSET(fd,?&tmp_inset)?>?0)
{
if?(fd?==?sockfd)
{?/*?服務端接收客戶端的連接請求?*/
if?((client_fd?=?accept(sockfd,
(struct?sockaddr?*)&client_sockaddr,?&sin_size))==?-1)
{
perror("accept");
exit(1);
}
FD_SET(client_fd,?&inset);
printf("New?connection?from?%d(socket)\n",?client_fd);
}
else?/*?處理從客戶端發來的消息?*/
{
if?((count?=?recv(client_fd,?buf,?BUFFER_SIZE,?0))?>?0)
{
printf("Received?a?message?from?%d:?%s\n",
client_fd,?buf);
}
else
{
close(fd);
FD_CLR(fd,?&inset);
printf("Client?%d(socket)?has?left\n",?fd);
}
}
}?/*?end?of?if?FD_ISSET*/
}?/*?end?of?for?fd*/
}?/*?end?if?while?while*/
close(sockfd);
exit(0);
}
運行該程序時,可以先啟動服務器端,再反復運行客戶端程序(這里啟動兩個客戶端進程)即可,服務器端運行結果如下所示:
$?./server
listening....
New?connection?from?4(socket)?????????????????/*?接受第一個客戶端的連接請求*/
Received?a?message?from?4:?Hello,First!????/*?接收第一個客戶端發送的數據*/
New?connection?from?5(socket)??????????????/*?接受第二個客戶端的連接請求*/
Received?a?message?from?5:?Hello,Second!???/*?接收第二個客戶端發送的數據*/
Client?4(socket)?has?left???????????????????/*?檢測到第一個客戶端離線了*/
Client?5(socket)?has?left???????????????????/*?檢測到第二個客戶端離線了*/
$?./client?localhost?Hello,First!?&?./client?localhost?Hello,Second
總結
以上是生活随笔為你收集整理的linux如何实现网络高级编程,嵌入式Linux网络编程之:网络高级编程-嵌入式系统-与非网...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 老版本mac如何升级11mac电脑如何更
- 下一篇: Mxnet基础知识(二)