• C Socket通信编程


    目录

    1、socket概述

    2、地址及顺序处理

    3、函数介绍

    4、使用实例


    1、socket概述

      1、TCP协议通过三次握手协议建立连接

    TCP协议通过三个报文段完成连接的建立,这个过程称为三次握手(three-way handshake),过程如下图所示。

    第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

    第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
    一个完整的三次握手也就是: 请求---应答---再次确认。

    对应的函数接口:
           

    从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

      2、编程流程

    2、地址及顺序处理

      1、地址结构相关处理

        struct socketaddr{

          unsigned short sa_family;

          char sa_data[14]

        }

        struct socketaddr_in{

          short int sa_family;//地址族,即使用什么样的地址,IPV4或者是IPV6

          unsigned short int sin_port;

          struct in_addr sin_addr;//存放IP地址

          unsigned char sin_zero[8]//填充0以保持也struct socketaddr 同样大小

        }

      2、数据存储优先顺序

        网络地址和主机地址转换

        #include <netinet/in.h>

        uint16_t htons(uint16_t host16bit)//主机地址向网络地址转换

        uint32_t htols(uint32_t host32bit)

        uint16_t ntohs(uint16_t net16bit)//网络地址向主机地址转换

        uint32_t ntohl(uint32_t net32bit)

        成功:返回要转换的字节序

        出错:-1

      3、地址格式转换

        将十进制表示的地址转换成二进制

        #include <arpa/inet.h>

        int inet_pton(int family,//协议类型

              const char *strptr,//要转化的值

              void *addrptr)//转化后的地址

        int inet_ntop(int family

               void *addrptr

               char *strptr

               size_t len)//转化后值的大小

        成功:0

        出错:-1

      4、名字地址转换

        gethostbyname()将主机名转换为IP地址

        gethostbyaddr()将IP地址转换为主机名

        #include <netbd.h>

        struct hostnet *gethostbyname(const char *honstname)

        成功:hostnet类型指针

        出错:-1

        int getaddrinfo(const char *hostname

                const char *service 

                const struct addrinfo *hints

                struct addrinfo **result)//返回的结果

        成功:0

        出错:-1

        struct hostnet {

          char *h_name;//主机名

          char **h_aliases;

          int h_addrtype;

          int h_length;

          char **h_addr_list;//指向IPV4的地址指针数组

        }


    3、函数介绍  

        1、socket()  

          #include<sys/socket.h>

          int socket(int family,int type,int protocal)

          成功:非法套接字描述符

          出错:-1;

        2、bind()

          #include<sys/socket.h>

          int bind(int sockefd,struct sockaddr *my_addr,int addrlen);

          成功:0;

          出错:-1

        3、listen()

          #include<sys/socket.h>

          int listen(int sockfd,int backlog)

        4、accept()

          #include<sys/socket.h>

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

        5、connect()

          #include<sys/socket.h>

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

          成功:0

          出错:-1

        6、send()

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

          成功:发送的字节数

         出错:-1

        7、recv()

          int recv(int sockfd,void *buff,int len,unsigned int flags)

          成功:接受的字节数

          出错:-1

        8、sendto()

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

          成功:发送的字节数

          出错:-1

        9、recvfrom()

          int recvfrom(int sockfd ,const void *msg,int len,unsigned int flags,struct sockaddr *from ,int * tolen)   

          成功:接受的字节数

          出错:-1


    4、简单实例

    1. /* File Name: server.c */  
    2. #include<stdio.h>  
    3. #include<stdlib.h>  
    4. #include<string.h>  
    5. #include<errno.h>  
    6. #include<sys/types.h>  
    7. #include<sys/socket.h>  
    8. #include<netinet/in.h>  
    9. #define DEFAULT_PORT 8000  
    10. #define MAXLINE 4096  
    11. int main(int argc, char** argv)  
    12. {  
    13.     int    socket_fd, connect_fd;  
    14.     struct sockaddr_in     servaddr;  
    15.     char    buff[4096];  
    16.     int     n;  
    17.     //初始化Socket  
    18.     if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){  
    19.     printf("create socket error: %s(errno: %d) ",strerror(errno),errno);  
    20.     exit(0);  
    21.     }  
    22.     //初始化  
    23.     memset(&servaddr, 0, sizeof(servaddr));  
    24.     servaddr.sin_family = AF_INET;  
    25.     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。  
    26.     servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT  
    27.   
    28.     //将本地地址绑定到所创建的套接字上  
    29.     if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){  
    30.     printf("bind socket error: %s(errno: %d) ",strerror(errno),errno);  
    31.     exit(0);  
    32.     }  
    33.     //开始监听是否有客户端连接  
    34.     if( listen(socket_fd, 10) == -1){  
    35.     printf("listen socket error: %s(errno: %d) ",strerror(errno),errno);  
    36.     exit(0);  
    37.     }  
    38.     printf("======waiting for client's request====== ");  
    39.     while(1){  
    40. //阻塞直到有客户端连接,不然多浪费CPU资源。  
    41.         if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){  
    42.         printf("accept socket error: %s(errno: %d)",strerror(errno),errno);  
    43.         continue;  
    44.     }  
    45. //接受客户端传过来的数据  
    46.     n = recv(connect_fd, buff, MAXLINE, 0);  
    47. //向客户端发送回应数据  
    48.     if(!fork()){ /*紫禁城*/  
    49.         if(send(connect_fd, "Hello,you are connected! ", 26,0) == -1)  
    50.         perror("send error");  
    51.         close(connect_fd);  
    52.         exit(0);  
    53.     }  
    54.     buff[n] = '';  
    55.     printf("recv msg from client: %s ", buff);  
    56.     close(connect_fd);  
    57.     }  
    58.     close(socket_fd);  
    59. }  

    客户端:

     

    1. /* File Name: client.c */  
    2.   
    3. #include<stdio.h>  
    4. #include<stdlib.h>  
    5. #include<string.h>  
    6. #include<errno.h>  
    7. #include<sys/types.h>  
    8. #include<sys/socket.h>  
    9. #include<netinet/in.h>  
    10.   
    11. #define MAXLINE 4096  
    12.   
    13.   
    14. int main(int argc, char** argv)  
    15. {  
    16.     int    sockfd, n,rec_len;  
    17.     char    recvline[4096], sendline[4096];  
    18.     char    buf[MAXLINE];  
    19.     struct sockaddr_in    servaddr;  
    20.   
    21.   
    22.     if( argc != 2){  
    23.     printf("usage: ./client <ipaddress> ");  
    24.     exit(0);  
    25.     }  
    26.   
    27.   
    28.     if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){  
    29.     printf("create socket error: %s(errno: %d) ", strerror(errno),errno);  
    30.     exit(0);  
    31.     }  
    32.   
    33.   
    34.     memset(&servaddr, 0, sizeof(servaddr));  
    35.     servaddr.sin_family = AF_INET;  
    36.     servaddr.sin_port = htons(8000);  
    37.     if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){  
    38.     printf("inet_pton error for %s ",argv[1]);  
    39.     exit(0);  
    40.     }  
    41.   
    42.   
    43.     if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){  
    44.     printf("connect error: %s(errno: %d) ",strerror(errno),errno);  
    45.     exit(0);  
    46.     }  
    47.   
    48.   
    49.     printf("send msg to server:  ");  
    50.     fgets(sendline, 4096, stdin);  
    51.     if( send(sockfd, sendline, strlen(sendline), 0) < 0)  
    52.     {  
    53.     printf("send msg error: %s(errno: %d) ", strerror(errno), errno);  
    54.     exit(0);  
    55.     }  
    56.     if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) {  
    57.        perror("recv error");  
    58.        exit(1);  
    59.     }  
    60.     buf[rec_len]  = '';  
    61.     printf("Received : %s ",buf);  
    62.     close(sockfd);  
    63.     exit(0);  
    64. }  

    测试:

    编译server.c

    gcc -o server server.c

    启动进程:

    ./server

    显示结果:

    ======waiting for client's request======

    并等待客户端连接。

    编译 client.c

    gcc -o client server.c

    客户端去连接server:

    ./client 127.0.0.1 

    等待输入消息

    发送一条消息,输入:c++

    此时服务器端看到:

    客户端收到消息:

     

    其实可以不用client,可以使用telnet来测试:

    telnet 127.0.0.1 8000

  • 相关阅读:
    lrzsz on linux
    ASP.Net Core 运行在Linux(CentOS)
    ASP.Net Core 运行在Linux(Ubuntu)
    .Net程序跑在Linux上
    通过GitHub部署网站到Azure WebSite
    kubernetes报错
    第4篇创建harbor私有镜像库
    第1篇Kubernetes介绍
    第2篇Kubernetes架构
    第3篇K8S集群部署
  • 原文地址:https://www.cnblogs.com/void0/p/4233238.html
Copyright © 2020-2023  润新知