zoukankan      html  css  js  c++  java
  • Linux网络编程简单示例

    linux 网络编程是通过socket(套接字)接口实现,Socket是一种文件描述符,socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。

     

    socket 类型

        常见的socket有3种类型如下。 
        (1)流式socket(SOCK_STREAM ) 
        流式套接字提供可靠的、面向连接的通信流;它使用TCP 协议,从而保证了数据传输的正确性和顺序性。 
        (2)数据报socket(SOCK_DGRAM ) 
        数据报套接字定义了一种无连接的服 ,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP。 
        (3)原始socket(SOCK_RAW)
        原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。

    进行Socket编程,常用的函数有:

    1.socket:创建一个socket

      int socket(int family, int type, int protocol);

    //family指定协议族;type参数指定socket的类型:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;protocol通常赋值"0", socket()调用返回一个整型socket描述符

    2.bind:用于绑定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置为相应的错误号

    3.connect:该函数用于绑定之后的client端,与服务器建立连接

    int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);

    //sockfd是目的服务器的sockect描述符;serv_addr是服务器端的IP地址和端口号的地址,addrlen常被设置为sizeof(struct sockaddr)。遇到错误时返回-1,并且errno中包含相应的错误码

    4.listen:设置能处理的最大连接数,listen()并未开始接受连线,只是设置sockect为listen模式

     int listen(int sockfd, int backlog); 

    // sockfd是socket系统调用返回的服务器端socket描述符;backlog指定在请求队列中允许的最大请求数    

    5.accept:用来接受socket连接

    int accept(int sockfd, struct sockaddr *addr, int *addrlen); 

    //sockfd是被监听的服务器socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求的客户端地址;addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。错误发生时返回一个-1并且设置相应的errno值

    6.send:发送数据         

    int send(int sockfd, const void *msg, int len, int flags); 

    //sockfd是你想用来传输数据的socket描述符,msg是一个指向要发送数据的指针。 len是以字节为单位的数据的长度。flags一般情况下置为0


    7.recv:接受数据        

    int recv(int sockfd,void *buf,int len,unsigned int flags); 

    //sockfd是接受数据的socket描述符;buf 是存放接收数据的缓冲区;len是缓冲的长度。flags也被置为0。recv()返回实际上接收的字节数,或当出现错误时,返回-1并置相应的errno值。

    8.sendto:发送数据,用于面向非连接的socket(SOCK_DGRAM/SOCK_RAW)

    int sendto(int sockfd, const void *msg,int len,unsigned int flags,const struct sockaddr *to,int tolen); 

    //该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。

    9.recvform: 接受数据,用于面向非连接的socket(SOCK_DGRAM/SOCK_RAW)

    int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen); 

    //from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof(struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno   

    几个字节顺序转换函数: 

        htons()   --"Host to Network Short" ; htonl()--"Host to Network Long" 
        ntohs()   --"Network to Host Short" ; ntohl()--"Network to Host Long" 
        在这里, h表示"host" ,n表示"network",s 表示"short",l表示 "long"。

    地址转换函数:

        in_addr_t inet_addr(const char * strptr);
        将字符串IP地址转换为IPv4地址结构in_addr值

        char * inet_ntoa(struct in_addr * addrptr);
        将IPv4地址结构in_addr值转换为字符串IP

        域名和IP地址的转换: 
        struct hostent *gethostbyname(const char *name); 
        函数返回一种名为hostent的结构类型,它的定义如下: 
        struct hostent 
        { 
             char *h_name;        /* 主机的官方域名 */ 
             char **h_aliases;    /* 一个以NULL结尾的主机别名数组 */ 
             int    h_addrtype;   /* 返回的地址类型,在Internet环境下为AF-INET */ 
             int h_length;        /*地址的字节长度 */ 
             char **h_addr_list;  /* 一个以0结尾的数组,包含该主机的所有地址*/ 
      }; 
        #define h_addr h_addr_list[0] /*在h-addr-list中的第一个地址*/

    下面给出两个示例,一个是面向TCP数据流的socket通信,一个是面向UDP数据报的socket通信

    1.面向TCP数据流的socket通信的演示程序由基于TCP的服务器和基于TCP的客户端程序组成。

        TCP的服务器程序结构:

        1.创建一个socket,用函数socket()

        2.绑定IP地址、端口信息到socket上,用函数bind()

        3.设置允许的最大连接数,用函数listen()

        4.接受客户端的连接,用函数accept()

        5.收发数据,用send()、recv()或者read()、write()

        6.关闭网络连接

        TCP的客户端程序结构:

        1.创建一个socket,用函数socket()

        2.设置要连接的服务器的IP地址和端口属性

        3.连接服务器,用函数connet()

        4.收发数据,用send()、recv()或者read()、write()

        5.关闭网络连接

    TCP服务器和TCP客户端的通信图如下:

    下面贴出TCP服务器的代码tcp_server.c:

    tcp_server.c

    1. #include <stdlib.h> 
    2. #include <stdio.h> 
    3. #include <errno.h> 
    4. #include <string.h> 
    5. #include <netdb.h> 
    6. #include <sys/types.h> 
    7. #include <netinet/in.h> 
    8. #include <sys/socket.h> 
    9. #include <arpa/inet.h> //inet_ntoa()函数的头文件
    10. #define portnumber 3333 //定义端口号:(0-1024为保留端口号,最好不要用)
    11. int main(int argc, char *argv[]) 
    12.     int sockfd,new_fd; 
    13.     struct sockaddr_in server_addr; //描述服务器地址
    14.     struct sockaddr_in client_addr; //描述客户端地址
    15.     int sin_size; 
    16.     char hello[]="Hello! Are You Fine? "; 
    17.     
    18.     /* 服务器端开始建立sockfd描述符 */ 
    19.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:IPV4;SOCK_STREAM:TCP
    20.     { 
    21.         fprintf(stderr,"Socket error:%s a",strerror(errno)); 
    22.         exit(1); 
    23.     } 
    24.     /* 服务器端填充 sockaddr结构 */ 
    25.     bzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化,置0
    26.     server_addr.sin_family=AF_INET; // Internet
    27.     server_addr.sin_addr.s_addr=htonl(INADDR_ANY); // (将本机器上的long数据转化为网络上的long数据)和任何主机通信 //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP
    28.     //server_addr.sin_addr.s_addr=inet_addr("192.168.1.1"); //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
    29.     server_addr.sin_port=htons(portnumber); // (将本机器上的short数据转化为网络上的short数据)端口号
    30.     
    31.     /* 捆绑sockfd描述符到IP地址 */ 
    32.     if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) 
    33.     { 
    34.         fprintf(stderr,"Bind error:%s a",strerror(errno)); 
    35.         exit(1); 
    36.     } 
    37.     /* 设置允许连接的最大客户端数 */ 
    38.     if(listen(sockfd,5)==-1) 
    39.     { 
    40.         fprintf(stderr,"Listen error:%s a",strerror(errno)); 
    41.         exit(1); 
    42.     } 
    43.     while(1) 
    44.     { 
    45.         /* 服务器阻塞,直到客户程序建立连接 */ 
    46.         sin_size=sizeof(struct sockaddr_in); 
    47.         if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1) 
    48.         { 
    49.             fprintf(stderr,"Accept error:%s a",strerror(errno)); 
    50.             exit(1); 
    51.         } 
    52.         fprintf(stderr,"Server get connection from %s ",inet_ntoa(client_addr.sin_addr)); // 将网络地址转换成.字符串,并打印到输出终端
    53.         
    54.         //向客户端程序写入hello数组里的字符
    55.         if(write(new_fd,hello,strlen(hello))==-1) 
    56.         { 
    57.             fprintf(stderr,"Write Error:%s ",strerror(errno)); 
    58.             exit(1); 
    59.         } 
    60.         /* 这个通讯已经结束 */ 
    61.         close(new_fd); 
    62.         /* 循环下一个 */ 
    63.     } 
    64.     /* 结束通讯 */ 
    65.     close(sockfd); 
    66.     exit(0); 
    67. }
    TCP客户端的代码tcp_client.c

    tcp_client.c

    1. #include <stdlib.h> 
    2. #include <stdio.h> 
    3. #include <errno.h> 
    4. #include <string.h> 
    5. #include <netdb.h> 
    6. #include <sys/types.h> 
    7. #include <netinet/in.h> 
    8. #include <sys/socket.h> 
    9. #include <arpa/inet.h> //inet_ntoa()函数的头文件
    10. #define portnumber 3333 //定义端口号:(0-1024为保留端口号,最好不要用)
    11. int main(int argc, char *argv[]) 
    12.     int sockfd; 
    13.     char buffer[1024]; 
    14.     struct sockaddr_in server_addr; //描述服务器的地址
    15.     struct hostent *host; 
    16.     int nbytes; 
    17.     /* 使用hostname查询host 名字 */
    18.     if(argc!=2) 
    19.     { 
    20.         fprintf(stderr,"Usage:%s hostname a ",argv[0]); 
    21.         exit(1); 
    22.     } 
    23.     if((host=gethostbyname(argv[1]))==NULL) 
    24.     { 
    25.         fprintf(stderr,"Gethostname error "); 
    26.         exit(1); 
    27.     } 
    28.     /* 客户程序开始建立 sockfd描述符 */ 
    29.     if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:Internet;SOCK_STREAM:TCP
    30.     { 
    31.         fprintf(stderr,"Socket Error:%sa ",strerror(errno)); 
    32.         exit(1); 
    33.     } 
    34.     /* 客户程序填充服务端的资料 */ 
    35.     bzero(&server_addr,sizeof(server_addr)); // 初始化,置0
    36.     server_addr.sin_family=AF_INET; // IPV4
    37.     server_addr.sin_port=htons(portnumber); // (将本机器上的short数据转化为网络上的short数据)端口号
    38.     server_addr.sin_addr=*((struct in_addr *)host->h_addr); // IP地址
    39.     
    40.     /* 客户程序发起连接请求 */ 
    41.     if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) 
    42.     { 
    43.         fprintf(stderr,"Connect Error:%sa ",strerror(errno)); 
    44.         exit(1); 
    45.     } 
    46.     /* 连接成功了 */ 
    47.     if((nbytes=read(sockfd,buffer,1024))==-1) 
    48.     { 
    49.         fprintf(stderr,"Read Error:%s ",strerror(errno)); 
    50.         exit(1); 
    51.     } 
    52.     buffer[nbytes]=''; 
    53.     printf("I have received:%s ",buffer); 
    54.     /* 结束通讯 */ 
    55.     close(sockfd); 
    56.     exit(0); 
    57. }

    编译tcp_server.c和tcp_client.c

    gcc tcp_server.c -o tcp_server

    gcc tcp_client.c -o tcp_client

    运行tcp服务器段程序和客户端程序,显示过程截图如下:




    2.面向UDP数据报的socket通信的演示程序由基于UCP的服务器和基于UDP的客户端程序组成。

        UDP的服务器程序结构:

        1.创建一个socket,用函数socket()

        2.绑定IP地址、端口信息到socket上,用函数bind()

        3.循环接受数据,用函数recvform()

        4.关闭网络连接

       UDP的客户端程序结构:

        1.创建一个socket,用函数socket()

        2.设置要连接的服务器的IP地址和端口属性

        3.发送数据,用函数sento()

        4.关闭网络连接

    UDP服务器和UDP客户端的通信图如下:


    下面贴出UDP服务器的代码udp_server.c:

    udp_serer.c

    1. #include <stdlib.h>
    2. #include <stdio.h>
    3. #include <errno.h>
    4. #include <string.h>
    5. #include <unistd.h>
    6. #include <netdb.h>
    7. #include <sys/socket.h>
    8. #include <netinet/in.h>
    9. #include <sys/types.h>
    10. #include <arpa/inet.h>
    11. #define SERVER_PORT 8888 //定义端口号:(0-1024为保留端口号,最好不要用)
    12. #define MAX_MSG_SIZE 1024 
    13. void udps_respon(int sockfd) 
    14.     struct sockaddr_in addr; 
    15.     int addrlen,n; 
    16.     char msg[MAX_MSG_SIZE]; 
    17.     while(1) 
    18.     {    /* 从网络上读,并写到网络上 */ 
    19.         bzero(msg,sizeof(msg)); // 初始化,清零
    20.         addrlen = sizeof(struct sockaddr); 
    21.         n=recvfrom(sockfd,msg,MAX_MSG_SIZE,0,(struct sockaddr*)&addr,&addrlen); // 从客户端接收消息
    22.         msg[n]=0; 
    23.         /* 显示服务端已经收到了信息 */ 
    24.         fprintf(stdout,"Server have received %s",msg); // 显示消息
    25.     } 
    26. int main(void) 
    27.     int sockfd; //socket描述符
    28.     struct sockaddr_in addr; //定义服务器起地址
    29.     /* 服务器端开始建立socket描述符 */ 
    30.     sockfd=socket(AF_INET,SOCK_DGRAM,0); 
    31.     if(sockfd<0) 
    32.     { 
    33.         fprintf(stderr,"Socket Error:%s ",strerror(errno)); 
    34.         exit(1); 
    35.     } 
    36.     /* 服务器端填充 sockaddr结构 */ 
    37.     bzero(&addr,sizeof(struct sockaddr_in)); // 初始化,置0
    38.     addr.sin_family=AF_INET; // Internet
    39.     addr.sin_addr.s_addr=htonl(INADDR_ANY); // (将本机器上的long数据转化为网络上的long数据)和任何主机通信 //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP
    40.     //addr.sin_addr.s_addr=inet_addr("192.168.1.1"); //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ip
    41.     addr.sin_port=htons(SERVER_PORT); // (将本机器上的short数据转化为网络上的short数据)端口号
    42.     /* 捆绑sockfd描述符 */ 
    43.     if(bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in))<0) 
    44.     { 
    45.         fprintf(stderr,"Bind Error:%s ",strerror(errno)); 
    46.         exit(1); 
    47.     } 
    48.     udps_respon(sockfd); // 进行读写操作
    49.     close(sockfd); 
    50. }
    UDP客户端的代码udp_client.c:

    udp_client.c

    1. #include <stdlib.h>
    2. #include <stdio.h>
    3. #include <errno.h>
    4. #include <string.h>
    5. #include <unistd.h>
    6. #include <netdb.h>
    7. #include <sys/socket.h>
    8. #include <netinet/in.h>
    9. #include <sys/types.h>
    10. #include <arpa/inet.h>
    11. #define SERVER_PORT 8888 //定义端口号:(0-1024为保留端口号,最好不要用)
    12. #define MAX_BUF_SIZE 1024 
    13. void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len) 
    14.     char buffer[MAX_BUF_SIZE]; 
    15.     int n; 
    16.     while(1) 
    17.     {     /* 从键盘读入,写到服务端 */ 
    18.         printf("Please input char: ");
    19.         fgets(buffer,MAX_BUF_SIZE,stdin); 
    20.         sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr*)addr,len); 
    21.         bzero(buffer,MAX_BUF_SIZE); 
    22.     } 
    23. int main(int argc,char **argv) 
    24.     int sockfd; //socket描述符
    25.     struct sockaddr_in addr; //定义服务器起地址
    26.     if(argc!=2) 
    27.     { 
    28.         fprintf(stderr,"Usage:%s server_ip ",argv[0]); 
    29.         exit(1); 
    30.     }
    31.     /* 建立 sockfd描述符 */ 
    32.     sockfd=socket(AF_INET,SOCK_DGRAM,0); 
    33.     if(sockfd<0) 
    34.     { 
    35.         fprintf(stderr,"Socket Error:%s ",strerror(errno)); 
    36.         exit(1); 
    37.     } 
    38.     /* 填充服务端的资料 */ 
    39.     bzero(&addr,sizeof(struct sockaddr_in)); // 初始化,置0
    40.     addr.sin_family=AF_INET; // Internet
    41.     addr.sin_port=htons(SERVER_PORT);// (将本机器上的short数据转化为网络上的short数据)端口号
    42.     if(inet_aton(argv[1],&addr.sin_addr)<0) /*inet_aton函数用于把字符串型的IP地址转化成网络2进制数字*/ 
    43.     { 
    44.         fprintf(stderr,"Ip error:%s ",strerror(errno)); 
    45.         exit(1); 
    46.     } 
    47.     udpc_requ(sockfd,&addr,sizeof(struct sockaddr_in)); // 进行读写操作
    48.     close(sockfd); 
    49. }

    编译udp_server.c和udp_client.c

    gcc udp_server.c -o udp_server

    gcc udp_client.c -o udp_client

    运行udp服务器段程序和客户端程序,显示过程截图如下:



  • 相关阅读:
    day08,文件操作。
    day07,基础数据部分的补充
    day06,1,小数据池,常量池。2,编码。
    day05,字典(dic)
    day04,1列表,2列表的增删改查,3列表的嵌套,4元祖,
    day03:python基础数据类型操作(索引,切片,迭代)
    day02_while循环 ,运算符,格式化输出
    变量的命名规则
    Day-23 基础模块4 模块导入_包
    Day-22 基础模块3 正则表达式_re模块
  • 原文地址:https://www.cnblogs.com/wanghuaijun/p/6021728.html
Copyright © 2011-2022 走看看