linux socket 编程(一)

socket详细介绍:http://blog.csdn.net/hguisu/article/details/7445768/

server_one.c

#include <stdlib.h>

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>


int main(int argc, char *argv[])
{
        int                     sockfd;
        int                     new_fd;
        struct sockaddr_in server_addr;
        不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct sockaddr_in) 来代替.在中有sockaddr_in的定义 
        struct sockaddr_in{
              unsigned short          sin_family;     
              unsigned short int      sin_port;
              struct in_addr          sin_addr;
              unsigned char           sin_zero[8];
        }
        我们主要使用Internet所以
            sin_family一般为AF_INET,
            sin_addr设置为INADDR_ANY表示可以和任何的主机通信,
            sin_port是我们要监听的端口号.sin_zero[8]是用来填充的.


        typedef uint32_t in_addr_t;
        struct in_addr
        {
            in_addr_t s_addr;
        };


        struct sockaddr_in client_addr;
        int                   sin_size;
        int                 portnumber;
        char hello[]="Hello! Are You Fine?\n";


        if(2!=argc)
        {
                fprintf(stderr, "Usage:%s portnumber\n", argv[0]);
                exit(1);
        }


        if((portnumber=atoi(argv[1]))<0)
        {
         /*atoi (表示 ascii to integer)是把字符串转换成整型数的一个函数. 参数nptr字符串,如果第一个非空格字符存在,是数字或者正负号则开始做类型转换,之后检测到非数字(包括结束符 \0) 字符时停止转换,返回整型数。否则,返回零。*/
                fprintf(stderr, "Usage:%s portnumber\n", argv[0]);
                exit(1);
        }


        /* 服务器端开始建立socket描述符 */
        if((sockfd=socket(AF_INET,  SOCK_STREAM, 0))==-1)  
        {
            /* int socket(int domain, int type,int protocol)
    domain:说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等).AF_UNIX只能够用於单一的Unix 系统进程间通信,而AF_INET是针对Internet的,因而可以允许在远程主机之间通信(当我们 man socket时发现 domain可选项是 PF_*而不是AF_*,因为glibc是posix的实现所以用PF代替了AF,不过我们都可以使用的).type:我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM等)  SOCK_STREAM表明我们用的是TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流. SOCK_DGRAM 表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信.protocol:由于我们指定了type,所以这个地方我们一般只要用0来代替就可以了 socket为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看errno可知道出错的详细情况.*/


                fprintf(stderr, "Socket error:%s\n", strerror(errno));
        /* stderr(sample standard error)默认输出到终端窗口,文件描述器代码为2.在C中,程序执行时,一直处于开启状态。 */
        /* 作用:通过标准错误的标号,获得错误的描述字符串 ,将单纯的错误标号转为字符串描述,方便用户查找错误。参数:错误标号,通常用errno(标准错误号,定义在errno.h中) 返回:指向错误信息的指针(即:错误的描述字符串) */
                exit(1);
        }


        /* 服务器端填充 sockaddr结构  */ 
        bzero(&server_addr,sizeof(struct sockaddr_in));
        server_addr.sin_family=AF_INET;
        server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
        /*htonl,其实是host to network, l 的意思是返回类型是long,
     将主机数转换成无符号长整型的网络字节顺序。本函数将一个32位数从主机字节顺序转换成网络字节顺序。所谓网络字节顺序(大尾顺序)就是指一个数在内存中存储的时候“高对低,低对高”(即一个数的高位字节存放于低地址单元,低位字节存放在高地址单元中)。但是计算机的内存存储数据时有可能是大尾顺序或者小尾顺序。在网络上使用统一的网络字节顺序,可以避免兼容性问题。*/
        server_addr.sin_port=htons(portnumber);


        /* 捆绑sockfd描述符  */ 
        if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
        /*用于绑定IP地址和端口号到socket.  int bind(int sockfd, struct sockaddr *my_addr, int addrlen);sockfd是一个socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的针; addrlen常被设置为sizeof(struct sockaddr),bind()函数在成功被调用时返回0;遇到错误时返回"-1"并将errno置为相应的错误号*/
        {
                fprintf(stderr,"Bind error:%s", strerror(errno));
                exit(1);
        }


        /* 监听sockfd描述符  */
        if(listen(sockfd,5)==-1)
        /* 设置能处理的最大连接数,listen()并未开始接受连线,只是设置sockect为listen模式.int listen(int sockfd, int backlog); sockfd是socket系统调用返回的服务器端socket描述符;backlog指定在请求队列中允许的最大请求数  */
        {
                fprintf(stderr,"Listen error:%s\a", strerror(errno));
                exit(1);
        }


        while(1)
        {
                /* 服务器阻塞,直到客户程序建立连接  */
                sin_size=sizeof(struct sockaddr_in);
                if((new_fd=accept(sockfd, (struct sockaddr *)(&client_addr), &sin_size))==-1)
                /*用来接受socket连接.int accept(int sockfd, struct sockaddr *addr, int *addrlen); sockfd是被监听的服务器socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求的客户端地址;addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。addr,addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了. bind,listen和accept是服务器端用的函数,accept调用时,服务器端的程序会一直阻塞到有一个 客户程序发出了连接. accept成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描述符写信息了. 错误发生时返回一个-1并且设置相应的errno值*/
                {
                        fprintf(stderr,"Accept error:%s\a", strerror(errno));
                        exit(1);
                }


                fprintf(stderr,"Server get connection from %s\n",
                inet_ntoa(client_addr.sin_addr));
                /* 函数声明:char *inet_ntoa (struct in_addr); 将网络地址转换成“.”点隔的字符串格式。若无错误发生,inet_ntoa()返回一个字符指针。否则的话,返回NULL。*/
                if(write(new_fd, hello, strlen(hello))==-1)
                {
                        fprintf(stderr,"Write Error:%s\n",strerror(errno));
                        exit(1);
                }
                /* 这个通讯已经结束     */
                close(new_fd);
                /* 循环下一个     */  
        }
        close(sockfd);
        exit(0);

}


client_one.c

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>


int main(int argc, char *argv[])
{
        int                     sockfd;
        char              buffer[1024];
        struct sockaddr_in server_addr;
        struct           hostent *host;
        /* hostent是host entry的缩写,该结构记录主机的信息,包括主机名、别名、地址类型、地址长度和地址列表。之所以主机的地址是一个列表的形式,原因是当一个主机有多个网络接口时,自然有多个地址。
        这个数据结构是这样的: 
        struct hostent { 
           char *h_name; 
           char **h_aliases; 
           int h_addrtype; 
           int h_length; 
           char **h_addr_list; 
       }; 
      #define h_addr h_addr_list[0]  
       这里是这个数据结构的详细资料:  
       struct hostent:  
          h_name – 地址的正式名称。 
          h_aliases – 空字节-地址的预备名称的指针。 
          h_addrtype –地址类型; 通常是AF_INET。  
          h_length – 地址的比特长度。 
          h_addr_list – 零字节-主机网络地址指针。网络字节顺序。表示的是主机的ip地址,注意,这个是以网络字节序存储的。千万不要直接用printf带%s参数来打这个东西,会有问题的哇。所以到真正需要打印出这个IP的话,需要调用inet_ntop()。 
            h_addr - h_addr_list中的第一地址。 
        gethostbyname()成功时返回一个指向结构体 hostent 的指针,或者是个空(NULL)指针。(但是和以前不同,不设置errno,h_errno 设置错误信息*/


        int                 portnumber;
        int                     nbytes;


        if(argc!=3)
        {
                fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
                exit(1);
        }


        if((host=gethostbyname(argv[1]))==NULL)
        {
                fprintf(stderr,"Gethostname error\n");
                exit(1);
        }


        if((portnumber=atoi(argv[2]))<0)
        {
                fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
                exit(1);
        }


        /* 客户程序开始建立 sockfd描述符  */
        if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
        {
                fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
                exit(1);
        }


        /* 客户程序填充服务端的资料       */
        bzero(&server_addr,sizeof(server_addr));
        /* extern void bzero(void *s, int n);置字节字符串前n个字节为零且包括‘\0’。s 要置零的数据的起始地址; n 要置零的数据字节个数。 */


        server_addr.sin_family=AF_INET;
        server_addr.sin_port=htons(portnumber);
        server_addr.sin_addr=*((struct in_addr *)host->h_addr);


        /* 客户程序发起连接请求         */ 
        if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
        /*  int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)
             sockfd:socket返回的文件描述符.
             serv_addr:储存了服务器端的连接信息.其中sin_add是服务端的地址
             addrlen:serv_addr的长度
             connect函数是客户端用来同服务端连接的.成功时返回0,sockfd是同服务端通讯的文件描述符 失败时返回-1.*/
            
        {
                fprintf(stderr,"Connect Error:%s\a\n", strerror(errno));
                exit(1);
        }


        /* 连接成功了           */
        if((nbytes=read(sockfd, buffer, 1024))==-1)
        {
                fprintf(stderr,"Read Error:%s\n", strerror(errno));
                exit(1);
        }
        buffer[nbytes]='\0';
        printf("I have received:%s\n",buffer);
        /* 结束通讯     */
        close(sockfd);
        exit(0);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章