搜索
您的当前位置:首页正文

Linux 下 Socke t编程

来源:步旅网

什么是Socket
    Socket
接口是TCP/IP网络的APISocket接口定义了许多函数或例程,程序员可以用它们来开发TCP/IP网络上的应用程序。要学Internet上的TCP/IP网络编程,必须理解Socket接口。
    Socket
接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话,就很容易了解Socket了。网络的Socket数据传输是一种特殊的I/OSocket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。常用的Socket类型有两种:流式SocketSOCK_STREAM)和数据报式SocketSOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。

结束传输
   
当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而停止在该socket上的任何数据操作:
close(sockfd);
   
你也可以调用shutdown()函数来关闭该socket。该函数允许你只停止在某个方向上的数据传输,而一个方向上的数据传输继续进行。如你可以关闭某socket的写操作而允许继续在该socket上接受数据,直至读入所有数据。
    intshutdown(intsockfd,inthow);
    Sockfd
是需要关闭的socket的描述符。参数how允许为shutdown操作选择以下几种方式:
    ·0-------
不允许继续接收数据
    ·1-------
不允许继续发送数据
    ·2-------
不允许继续发送和接收数据,
    ·
均为允许则调用close()
    shutdown
在操作成功时返回0,在出现错误时返回-1并置相应errno

阻塞和非阻塞
   
阻塞函数在完成其指定的任务以前不允许程序调用另一个函数。例如,程序执行一个读数据的函数调用时,在此函数完成读操作以前将不会执行下一程序语句。当服务器运行到accept语句时,而没有客户连接服务请求到来,服务器就会停止在accept语句上等待连接服务请求的到来。这种情况称为阻塞(blocking)。而非阻塞操作则可以立即完成。比如,如果你希望服务器仅仅注意检查是否有客户在等待连接,有就接受连接,否则就继续做其他事情,则可以通过将Socket设置为非阻塞方式来实现。非阻塞socket在没有客户在等待时就使accept调用立即返回。
    #include<unistd.h>
    #include<fcntl.h>
    ……
sockfd=socket(AF_INET,SOCK_STREAM,0);
fcntl(sockfd,F_SETFL,O_NONBLOCK)

……
   
通过设置socket为非阻塞方式,可以实现"轮询"若干Socket。当企图从一个没有数据等待处理的非阻塞Socket读入数据时,函数将立即返回,返回值为-1,并置errno值为EWOULDBLOCK。但是这种"轮询"会使CPU处于忙等待方式,从而降低性能,浪费系统资源。而调用select()会有效地解决这个问题,它允许你把进程本身挂起来,而同时使系统内核监听所要求的一组文件描述符的任何活动,只要确认在任何被监控的文件描述符上出现活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了为进程选出随机的变化,而不必由进程本身对输入进行测试而浪费CPU开销。Select函数原型为:
intselect(intnumfds,fd_set*readfds,fd_set*writefds

fd_set*exceptfds,structtimeval*timeout);
   
其中readfdswritefdsexceptfds分别是被select()监视的读、写和异常处理的文件描述符集合。如果你希望确定是否可以从标准输入和某个socket描述符读取数据,你只需要将标准输入的文件描述符0和相应的sockdtfd加入到readfds集合中;numfds的值是需要检查的号码最高的文件描述符加1,这个例子中numfds的值应为sockfd+1;当select返回时,readfds将被修改,指示某个文件描述符已经准备被读取,你可以通过FD_ISSSET()来测试。为了实现fd_set中对应的文件描述符的设置、复位和测试,它提供了一组宏:
    FD_ZERO(fd_set*set)----
清除一个文件描述符集;
    FD_SET(intfd,fd_set*set)----
将一个文件描述符加入文件描述符集中;
    FD_CLR(intfd,fd_set*set)----
将一个文件描述符从文件描述符集中清除;
    FD_ISSET(intfd,fd_set*set)----
试判断是否文件描述符被置位。
    Timeout
参数是一个指向structtimeval类型的指针,它可以使select()在等待timeout长时间后没有文件描述符准备好即返回。structtimeval数据结构为:
    structtimeval{
    inttv_sec;/*seconds*/
    inttv_usec;/*microseconds*/
};

POP3客户端实例
   
下面的代码实例基于POP3的客户协议,与邮件服务器连接并取回指定用户帐号的邮件。与邮件服务器交互的命令存储在字符串数组POPMessage中,程序通过一个do-while循环依次发送这些命令。
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#definePOP3SERVPORT110
#defineMAXDATASIZE4096

main(intargc,char*argv[]){
intsockfd;
structhostent*host;
structsockaddr_inserv_addr;
char*POPMessage[]={
"USERuserid/r/n",
"PASSpassword/r/n",
"STAT/r/n",
"LIST/r/n",
"RETR1/r/n",
"DELE1/r/n",
"QUIT/r/n",
NULL
};
intiLength;
intiMsg=0;
intiEnd=0;
charbuf[MAXDATASIZE];

if((host=gethostbyname("your.server"))==NULL){
perror("gethostbynameerror");
exit(1);
}
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
perror("socketerror");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(POP3SERVPORT);
serv_addr.sin_addr=*((structin_addr*)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
if(connect(sockfd,(structsockaddr*)&serv_addr,sizeof(structsockaddr))==-1){
perror("connecterror");
exit(1);
}

do{
send(sockfd,POPMessage[iMsg],strlen(POPMessage[iMsg]),0);
printf("havesent:%s",POPMessage[iMsg]);

iLength=recv(sockfd,buf+iEnd,sizeof(buf)-iEnd,0);
iEnd+=iLength;
buf[iEnd]='/0';
printf("received:%s,%d/n",buf,iMsg);

iMsg++;
}while(POPMessage[iMsg]);

close(sockfd);
}

 

因篇幅问题不能全部显示,请点此查看更多更全内容

Top