Linux下的socket编程

  1. Linux下的网络编程的含义
    linux 网络编程是通过socket(套接字)接口实现,socket是一种文件描述符(可类比文件操作),socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种”打开—读/写—关闭”模式的实现,服务器和客户端各自维护一个”文件”,在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。
  2. 常见 socket 分类
    (1)流式socket(SOCK_STREAM )
    流式套接字提供可靠的、面向连接的通信流;它使用TCP 协议,从而保证了数据传输的正确性和顺序性。
    (2)数据报socket(SOCK_DGRAM )
    数据报套接字定义了一种无连接的服 ,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP。
    (3)原始socket(SOCK_RAW)
    原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。
  3. socket 编程常用函数
    (1)int socket(int family, int type, intprotocol); 用以创建一个socket
    family指定协议族;type参数指定socket的类型:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW;protocol通常赋值0, socket()调用返回一个整型socket描述符
    (2)int bind(int sockfd, struct sockaddr*my_addr, int addrlen); 用于绑定IP地址和端口号到socket
    sockfd是一个socket描述符,my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的针; addrlen常被设置为sizeof(struct sockaddr),bind()函数在成功被调 用时返回0;遇到错误时返回”-1”并将errno置为相应的错误号
    (3)int connect(int sockfd, struct sockaddr*serv_addr, int addrlen); 该函数用于绑定之后的client端,与服务器建立连接
    sockfd是目的服务器的sockect描述符;serv_addr是服务器端的IP地址和端口号的地址,addrlen常被设置为sizeof(struct sockaddr)。遇到错误时返回-1,并且errno中包含相 应的错误码
    (4)int listen(int sockfd, int backlog); 设置能处理的最大连接数,listen()并未开始接受连线,只是设置sockect为listen模式
    sockfd是socket系统调用返回的服务器端socket描述符;backlog指定在请求队列中允许的最大请求数
    (5)int accept(int sockfd, struct sockaddr*addr, int *addrlen); 用来接受socket连接
    sockfd是被监听的服务器socket描述符,addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求的客户端地址;addrten通常为一个指向值为 sizeof(struct sockaddr_in)的整型指针变量。错误发生时返回一个-1并且设置相应的errno值
    (6)int send(int sockfd, const void *msg, intlen, int flags); 发送数据
    sockfd是你想用来传输数据的socket描述符,msg是一个指向要发送数据的指针。 len是以字节为单位的数据的长度。flags一般情况下置为0
    (7)int recv(int sockfd,void *buf,intlen,unsigned int flags); 接收数据
    sockfd是接受数据的socket描述符;buf 是存放接收数据的缓冲区;len是缓冲的长度。flags也被置为0。recv()返回实际上接收的字节数,或当出现错误时,返回-1并置相应的errno值。
    (8)int sendto(int sockfd, const void *msg,intlen,unsigned int flags,const struct sockaddr *to,int tolen); 发送数据,用于面向非连接的socket(SOCK_DGRAM/SOCK_RAW)
    该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。
    (9)int recvfrom(int sockfd,void *buf,intlen,unsigned int flags,struct sockaddr *from,int *fromlen); 接受数据,用于面向非连接的socket(SOCK_DGRAM/SOCK_RAW)
    from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。fromlen常置为sizeof(struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno
  4. 其他数据结构各函数
    (1)in_addr_t inet_addr(const char *strptr); 将字符串IP地址转换为IPv4地址结构in_addr值
    (2)char * inet_ntoa(struct in_addr *addrptr); 将IPv4地址结构in_addr值转换为字符串IP
    (3)struct hostent *gethostbyname(const char*name); 域名和IP地址的转换
    函数返回一种名为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中的第一个地址
    (4)相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换。
    原因如下:网络协议规定接收到得第一个字节是高字节,存放到低地址,所以发送时会首先去低地址取数据的高字节。小端模式的多字节数据在存放时,低地址存放的是低字节,而被发送方网络协议函数发送时会首先去低地址取数据(想要取高字节,真正取得是低字节),接收方网络协议函数接收时会将接收到的第一个字节存放到低地址(想要接收高字节,真正接收的是低字节),所以最后双方都正确的收发了数据。而相同平台进行通信时,如果双方都进行转换最后虽然能够正确收发数据,但是所做的转换是没有意义的,造成资源的浪费。而不同平台进行通信时必须进行转换,不转换会造成错误的收发数据,字节序转换函数会根据当前平台的存储模式做出相应正确的转换,如果当前平台是大端,则直接返回不进行转换,如果当前平台是小端,会将接收到得网络字节序进行转换。
    htons() –将一个16位数从主机字节顺序转换成网络字节顺序。 返回值:htons()返回一个网络字节顺序的值
    ntohs() –本函数将一个16位数由网络字节顺序转换为主机字节顺序。 返回值:ntohs()返回一个以主机字节顺序表达的数
  5. 基于TCP的socket 的客户端和服务端实现过程
    (1)TCP的服务器程序结构
    ——创建一个socket,用函数socket()
    ——绑定IP地址、端口信息到socket上,用函数bind()
    ——设置允许的最大连接数,用函数listen()
    ——接受客户端的连接,用函数accept()
    ——收发数据,用send()、recv()或者read()、write()
    ——关闭网络连接
    (2) TCP的客户端程序结构:
    ——创建一个socket,用函数socket()
    ——设置要连接的服务器的IP地址和端口属性
    ——连接服务器,用函数connet()
    ——收发数据,用send()、recv()或者read()、write()
    ——关闭网络连接
  6. 基于UDP的socket 的客户端和服务端的实现过程
    (1)UDP的服务器程序结构
    ——创建一个socket,用函数socket()
    ——绑定IP地址、端口信息到socket上,用函数bind()
    ——循环接受数据,用函数recvform()
    ——关闭网络连接
    (2)UDP的客户端程序结构:
    ——创建一个socket,用函数socket()
    ——设置要连接的服务器的IP地址和端口属性
    ——发送数据,用函数sento()
    ——关闭网络连接
    1. 基于TCP的socket 服务端和客户端实现
      ——服务端
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include 
#include 

#define PORTNUM 6789
#define MAX_SIZE 1024
int main(int argc, char *argv[]) 
{ 
    int sockfd,new_fd; 
    struct sockaddr_in server_addr; 
    struct sockaddr_in client_addr;
    int sin_size; 
    char msg[MAX_SIZE] = "Hello! ...\n"; 
    //未做异常判断 socket() bind() listen() accept() wirte()错误返回值为-1 
    sockfd=socket(AF_INET,SOCK_STREAM,0); 

    bzero(&server_addr,sizeof(struct sockaddr_in));
    server_addr.sin_family=AF_INET; 
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY); //
    //server_addr.sin_addr.s_addr=inet_addr("127.0.0.1"); 
    server_addr.sin_port=htons(PORTNUM); 

   int ret = bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)); 

    ret = listen(sockfd,5); 

    while(1) 
    { 
        sin_size=sizeof(struct sockaddr_in); 
        new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size); 
        fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));

        ret = write(new_fd,msg, strlen(msg)); 
        while(1)
        {
            bzero(msg, MAX_SIZE);
            fgets(msg, MAX_SIZE, stdin);
            msg[strlen(msg) - 1] = '\0';
            printf("inputed: %s----input \"exit\" to quit\n", msg);
            if(strcmp(msg, "exit") == 0) break;
            ret = write(new_fd, msg, strlen(msg));
        }
        close(new_fd); 
    } 

    close(sockfd); 
    exit(0); 
}

——客户端

#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  


#define PORTNUM 6789 

int main(int argc, char *argv[]) 
{ 
    int sockfd; 
    char buffer[1024]; 
    struct sockaddr_in server_addr; 
    struct hostent *host; 
    int nbytes; 


    host=gethostbyname(argv[1]);//返回值不为NULL时正确 

    sockfd=socket(AF_INET,SOCK_STREAM,0);

    bzero(&server_addr,sizeof(server_addr)); 
    server_addr.sin_family=AF_INET; // IPV4
    server_addr.sin_port=htons(PORTNUM); //
    server_addr.sin_addr=*((struct in_addr *)host->h_addr); // IP地址

    int ret = connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)); 

    while((nbytes = read(sockfd, buffer, 1024)) != -1) 
    {
        if(nbytes > 0) 
        {
            buffer[nbytes]='\0'; 
            printf("client received:%s\n",buffer); 
            bzero(buffer, 1024);
        }
    }


    close(sockfd); 
    exit(0); 
}
  1. 基于UDP的socket 服务端和客户端实现

    ——服务端

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_PORT 7890 
#define MAX_MSG_SIZE 1024 


int main(void) 
{ 
    int sockfd; 
    struct sockaddr_in addr; 

    sockfd=socket(AF_INET,SOCK_DGRAM,0); 

    bzero(&addr,sizeof(struct sockaddr_in)); // 初始化,置0
    addr.sin_family=AF_INET; // Internet
    addr.sin_addr.s_addr=htonl(INADDR_ANY); 
    //addr.sin_addr.s_addr=inet_addr("127.0.0.1"); 
    addr.sin_port=htons(SERVER_PORT); 

    int ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(struct sockaddr_in)); 


    struct sockaddr_in client_addr; 
    int addrlen,n; 
    char msg[MAX_MSG_SIZE]; 

    while(1) 
    {   
        bzero(msg,sizeof(msg)); // 初始化,清零
        addrlen = sizeof(struct sockaddr); 
        n=recvfrom(sockfd,msg,MAX_MSG_SIZE,0,(struct sockaddr*)&client_addr,&addrlen); // 从客户端接收消息
        msg[n]=0; 
        fprintf(stdout,"Server received %s",msg); // 显示消息
    } 

    close(sockfd); 
}

——客户端

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define SERVER_PORT 7890 
#define MAX_BUF_SIZE 1024 


int main(int argc,char **argv) 
{ 
    int sockfd; 
    struct sockaddr_in addr; 

    sockfd=socket(AF_INET,SOCK_DGRAM,0); 

    bzero(&addr,sizeof(struct sockaddr_in)); // 初始化,置0
    addr.sin_family=AF_INET; // Internet
    addr.sin_port=htons(SERVER_PORT);
    int ret = inet_aton(argv[1],&addr.sin_addr); 


    char buffer[MAX_BUF_SIZE]; 
    int n; 
    while(1) 
    {   
        fgets(buffer,MAX_BUF_SIZE,stdin); 
        struct sockadd_int *t_addr = &addr;
        sendto(sockfd,buffer,strlen(buffer),0,(struct sockaddr*)t_addr,sizeof(struct sockaddr_in)); 
        bzero(buffer,MAX_BUF_SIZE); 
    } 

    close(sockfd); 
}

你可能感兴趣的:(C/C++)