Chinaunix首页 | 论坛 | 博客
  • 博客访问: 386269
  • 博文数量: 124
  • 博客积分: 2911
  • 博客等级: 少校
  • 技术积分: 1050
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-15 15:57
文章分类

全部博文(124)

文章存档

2012年(6)

2011年(26)

2010年(92)

我的朋友

分类: 系统运维

2010-06-14 21:57:12



int socket(int domain, int type,int protocol)
说明:
1.    domain:通讯协族(AF_UNIX | AF_INET | ...etc.).
    AF_UNIX用于Unix系统IPC ; AF_INET用于Internet,允许远程主机间通信
    当man socket时发现domain选项是PF_*,是因为glibc是posix的实现,不过我们都可以使用的
2.
    type:通讯协议(SOCK_STREAM | SOCK_DGRAM | ...etc.)
    SOCK_STREAM: TCP协议 ; SOCK_DGRAM: UDP协议
3.
    protocol: 由于指定了type,所以这里一般只要用0来代替
4.
    成功,返回文件描述符;失败,返回 -1,看error可知道出错的详细情况.
/********************************************************************************************************************************************/
int bind(int sockfd, struct sockaddr *my_addr, int addrlen)
说明:
1.    sockfd:文件描述符.
2.    addrlen: struct sockaddr的长度.
3.    my_addr:指向sockaddr的指针.
    struct sockaddr{
        unisgned short as_family;
        char sa_data[14];
    };
    由于系统的兼容性,一般使用struct sockaddr_in来代替
    struct sockaddr_in{
        unsigned short sin_family;    //一般为AF_INET(internet)
        unsigned short int sin_port;    //要监听的端口号
        struct in_addr sin_addr;        //一般为INADDR_ANY表示可以和任何的主机通信
        unsigned char sin_zero[8];    //用bzero填充
    };
    struct in_addr
    {
        unsigned long s_addr;
    };
4.
    成功返回0,失败返回 -1,看error可知道出错的详细情况.
/********************************************************************************************************************************************/
int listen(int sockfd, int backlog);

1.    sockfd:调用socket() 返回的套接字文件描述符
2.    backlog:队列允许的连接数目。解释:进入的连接在队列等待accept(),其数目限制于队列的允许。多数系统允许数目20。
3.    发生错误的时候返回-1,并设置全局错误变量errno
/********************************************************************************************************************************************/
int accept(int sockfd, struct sockaddr *addr,int *addrlen)
说明:client通过listen()的端口号connect()到server,其连接进入等待accept()的队列中.
1.    sockfd:listen后的文件描述符.
2.    addr, addrlen用来给客户端的程序填写,服务器端只要传递指针就可以了.
3.    成功时返回最后的服务器端的fd,此时可以向该fd写信息了.失败时返回-1
/********************************************************************************************************************************************/
int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)

1.    sockfd
2.    serv_addr:保存目的地端口和IP地址
3.    addrlen:sizeof(struct sockaddr)。
4.    成功返回0,失败时返回-1.


/**********************************************************************************************************************************************/
===================
||  服务器和客户机的信息函数 ||
===================

字节序转换函数
unsigned long  int htonl(unsigned long  int hostlong)        //将本机器的long数据转化为网络上的long
unsigned short int htons(unisgned short int hostshort)
unsigned long  int ntohl(unsigned long  int netlong)
unsigned short int ntohs(unsigned short int netshort)

h:host
n:network
s:short
l:long
/**********************************************************************************************************************************************/
IP与域名的转换:在网络上标志一台机器可以用IP或者是用域名.
    struct hostent *gethostbyname(const char *hostname)            //将机器名(如 linux.yessun.com)转换为一个结构指针,此结构储存了域名的信息
    struct hostent *gethostbyaddr(const char *addr,int len,int type)    //将32位IP地址转换为结构指针.
说明:
    这两个函数失败时返回NULL 且设置h_errno错误变量,调用h_strerror()可以得到详细的出错信息

struct hostent{
    char *h_name;    /* 主机名称:ip或域名*/
    char *h_aliases;    /* 主机的别名*/
    int   h_addrtype;    /* 主机地址类型(AF_INET...)*/
    int   h_length;        /*主机地址长度,对于IP4是4字节32位*/
    char **h_addr_list;    /*主机的IP地址列表 */
}
#define h_addr h_addr_list[0]  /* 主机的第一个IP地址*/
/**********************************************************************************************************************************************/
字符串IP和32位IP转换.
    int inet_aton(const char *cp,struct in_addr *inp)        //将a.b.c.d的IP转换为32位的IP,存储在inp指针里面
    char *inet_ntoa(struct in_addr in)                    //将32位IP转换为a.b.c.d的格式
/**********************************************************************************************************************************************/

获得服务信息函数(端口 | IP | 服务信息...etc.)

    int getsockname(int sockfd,struct sockaddr *localaddr,int *addrlen)
    int getpeername(int sockfd,struct sockaddr *peeraddr, int *addrlen)

    struct servent *getservbyname(const char *servname,const char *protoname)    //得到指定服务的端口号
    struct servent *getservbyport(int port,const char *protoname)                    //得到指定端口号的服务
说明:
    一般很少用这几个函数.对应客户端,当我们要得到连接的端口号时在connect调用成功后使用可得到系统分配的端口号.
    对于服务端,用INADDR_ANY填充后,可在accept调用成功后得到IP地址.

struct servent
{
    char *s_name;        //服务名
    char **s_aliases;    //别名列表
    int s_port;        //端口号
    char *s_proto;        //使用的协议
};
一个例子:
========================getServerInfo_internet.c========================
#include
#include
#include

int main(int argc ,char **argv)
{
    struct sockaddr_in addr;
    struct hostent *host;
    char **alias;
       
    if(argc<2)
    {
        fprintf(stderr,"Usage:%s hostname|ip..\n\a",argv[0]);
        exit(1);
    }
       
    argv++;
    for(;*argv!=NULL;argv++)
    {
        if(inet_aton(*argv,&addr.sin_addr)!=0)
        {
            host=gethostbyaddr((char   *)&addr.sin_addr,4,AF_INET);
            printf("Address information of Ip %s\n",*argv);
        }
        else
            host=gethostbyname(*argv); printf("Address information of host %s\n",*argv);
        if(host==NULL)
        {
            fprintf(stderr,"No address information of %s\n",*argv);
            continue;
        }
        printf("Official host name %s\n",host->h_name);

        printf("Name aliases:");
        for(     alias = host->h_aliases; *alias!=NULL ; alias++)
            printf("%s ,",*alias);
        printf("\n");

        printf("\nIp address:\n");
        alias=host->h_addr_list;
        for(; *alias!=NULL; alias++)
            printf("%s ,",inet_ntoa(*(struct in_addr *)(*alias)));
        printf("\n");
    }
}

/*****************************以下未读过**********************************/

一旦我们建立了连接,我们的下一步就是进行通 信了.在Linux下面把我们前面建立的通道看成是文件描述符,
这样服务器端和客户端进行通信时候,只要往文件描述符里面读写东西了.    就象我们往文件读写一样.   


4.1    写函数write   

    ssize_t    write(int    fd,const    void    *buf,size_t    nbytes)

write函数将 buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数.
失败时返回-1.    并设置errno变量.    在网络程序中,当我们向套接字文件描述符写时有俩种可能.   

1)write的返回值大于0,表示写了部分或者是全部的数据.   

2)返回的值小于0,此时出现了错误.我们要根据错误类型来处理.   

如果错误为EINTR表示在写的时候出现了 中断错误.   

如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接).   

为了处理以上的情况,我们 自己编写一个写函数来处理这几种情况.   


int    my_write(int    fd,void    *buffer,int    length)
{
    int    bytes_left;
    int    written_bytes;
    char    *ptr;

    ptr=buffer;
    bytes_left=length;
    while(bytes_left>0)
    {
        /*    开始写*/
        written_bytes=write(fd,ptr,bytes_left);
        if(written_bytes<=0)    /*    出错了*/
        {                           
            if(errno==EINTR)    /*    中断错误    我们继续写*/
                written_bytes=0;
            else            /*    其他错误    没有办法,只好撤退了*/
                return(-1);
        }
        bytes_left-=written_bytes;
        ptr+=written_bytes;                    /*    从剩下的地方继续写        */
    }
    return(0);
}

4.2    读函数read   
ssize_t    read(int    fd,void    *buf,size_t    nbyte)    read函数是负责从fd中读取内容.当读成功时,    read返回实际所读的字节数,如果返回的值是0    表示已经读到文件的结束了,小于0表示出现了错误.如果错误为EINTR说明读是由中断引起的,    如果是ECONNREST表示网络连接出了问题.    和上面一样,我们也写一个自己的读函数.   

int    my_read(int    fd,void    *buffer,int    length)
{
    int    bytes_left;
    int    bytes_read;
    char    *ptr;
       
    bytes_left=length;
    while(bytes_left>0)
    {
        bytes_read=read(fd,ptr,bytes_read);
        if(bytes_read<0)
        {
            if(errno==EINTR)
                        bytes_read=0;
            else
                        return(-1);
        }
        else    if(bytes_read==0)
        {
            break;
        }   
            bytes_left-=bytes_read;
            ptr+=bytes_read;
    }
    return(length-bytes_left);
}

4.3    数据的传递   
有了上面的两个函数,我们就可以向客户端或者是服务端传递数据了.比如我们要传递一个结构.可以使用如下方式   

    /*        客户端向服务端写    */

    struct    my_struct    my_struct_client;
    write(fd,(void    *)&my_struct_client,sizeof(struct    my_struct);

    /*    服务端的读*/   
    char    buffer[sizeof(struct    my_struct)];
    struct    *my_struct_server;
    read(fd,(void    *)buffer,sizeof(struct    my_struct));   
    my_struct_server=(struct    my_struct    *)buffer;               

在 网络上传递数据时我们一般都是把数据转化为char类型的数据传递.接收的时候也是一样的    注意的是我们没有必要在网络上传递指针(因为传递指针是没有任何意义的,我们必须传递指针所指向的内容)       


阅读(1595) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~