当前位置:   article > 正文

TCP通信模型(C语言实现)_c语言tcp通信

c语言tcp通信

        大家好,我是练习编程时长两年半的个人练习生昆工第一ikun,今天我们来分享TCP通信模型,并且用C语言实现它。


目录

一、我们将实现三个示例功能:

二、TCP服务器搭建流程

(1)创建套接字 --> socket();

(2)套接字绑定 -->bind() 核心:IP地址与PORT端口

(3)监听客户端连接请求 -->listen

(4)接收客户端连接请求 -->accept


​​​​​​​

一、我们将实现三个示例功能:

1.客户端给服务器发送一个字符串,服务器返回给客户端这个字符串的长度。

2.实现一个时间服务器,客户端发送time,服务器返回当前时间。

3.如果客户端发送get 1.txt的请求,服务器获取文件内容后,发送给客户端。

二、TCP服务器搭建流程

(1)创建套接字 --> socket();

#include  <sys/types.h>         
#include <sys/socket.h>
​
int socket(int family, int type, int protocol);
​
参数:
    family:协议族
        AF_INET:IPv4协议
        AF_INET6:IPv6协议
        AF_LOCAL:UNIX域协议
        AF_ROUTE:路由套接字
        AF_KEY:密钥套接字
    type:套接字类型
        SOCK_STREAM:流式套接字
        SOCK_DGRAM:数据报套接字
        SOCK_RAM:原始套接字
    protocol:0(原始套接字除外)
        
返回值:
        成功返回非负套接字描述符
        失败返回-1;
         

(2)套接字绑定 -->bind() 核心:IP地址与PORT端口

  • 端口:标识进程 无符号短整型: 0 ~ 65535 0 ~ 1024 被内核使用. 用户可指定端口:1025 ~ 65535

    字节序:大端序 与 小端序 
    ​
    网络字节序通常是大端序: 大端就是指低地址存放高字节 
    ​
    个人PC的字节序通常是小端序: 小段就是指低地址存放低字节 
    ​
    结论:需要在网络绑定port端口时进行字节序的转换..... 
    
  • 字节序转换函数:htons、htonl 等....

    可以使用man手册直接查看:man 3 htons 
    ​
    h: 主机host 
    ​
    to: 转换 
    ​
    n: net网络 
    ​
    s: short类型 
    ​
    htons就是把主机字节序转为网络字节序. 
    
  • IP地址:标识主机 ipv4占四个字节.

    点分十进制:"192.168.2.2" 是给人类看的 
    ​
    二进制形式:11000000 10101000 00000010 00000010 是计算机使用的 
    
  • IP地址转换函数

    点分转为二进制: 
    ​
    in_addr_t inet_addr(const char *cp); 
    ​
    cp: ip地址的字符串形式 
    ​
    in_addr_t: 转换后的二进制地址形式
    
        #include <sys/types.h>         
        #include <sys/socket.h>
​
        int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
​
        参数:
            sockfd:套接字描述符
            my_addr:绑定的地址
            addrlen:地址长度
​
        返回值:
                成功返回0;
                失败返回-1;
        

(3)监听客户端连接请求 -->listen

#include <sys/types.h>        
#include <sys/socket.h>
​
int listen(int sockfd, int backlog);
​
参数:
    sockfd:套接字描述符
    backlog:请求队列中允许的最大请求数,大多数系统默认值为5;
    
返回值:
    成功返回0;失败返回-1;
  

(4)接收客户端连接请求 -->accept

        注意:后续的数据通信全部使用通信套接字,不能使用监听套接字来通信

#include <sys/types.h>
#include <sys/socket.h>
​
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
​
参数:
    sockfd:套接字描述符
    addr:用于保护客户端地址,如果不需要客户端信息则传递 NULL
    addrlen:地址长度,如果不需要客户端信息则传递 NULL
    
返回值:
    成功返回用于通信的套接字(连接套接字 或 通信套接字)
    失败返回-1;
    

我们先来创建一个服务器:

  1. #include <strings.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include <sys/types.h> /* See NOTES */
  6. #include <sys/socket.h>
  7. #include <netinet/in.h>
  8. #include <unistd.h>
  9. #include <arpa/inet.h>
  10. #include <time.h>
  11. #include <sys/stat.h>
  12. #include <fcntl.h>
  13. #include <pthread.h>
  14. void *send_data(void *arg)
  15. {
  16. int connfd = *(int*)arg;
  17. char s[64] = {0};
  18. while(1)
  19. {
  20. fgets(s, 64, stdin);
  21. s[strlen(s)-1] = '\0';
  22. write(connfd, s, 64);
  23. memset(s, 0, 64);
  24. }
  25. }
  26. int main(int argc, char *argv[])
  27. {
  28. int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  29. if(sockfd == -1)
  30. {
  31. perror("socket");
  32. return -1;
  33. }
  34. //端口复用函数
  35. int on = 1;
  36. int k = setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR, &on, sizeof(on));
  37. if(k == -1)
  38. {
  39. perror("setsockopt");
  40. return -1;
  41. }
  42. struct sockaddr_in ser;
  43. ser.sin_family = AF_INET;
  44. ser.sin_port = htons(11111);
  45. ser.sin_addr.s_addr = inet_addr("0.0.0.0");
  46. int ret = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
  47. if(ret == 0)
  48. {
  49. printf("绑定成功\n");
  50. }
  51. else
  52. {
  53. perror("bind");
  54. return -1;
  55. }
  56. ret = listen(sockfd, 5);
  57. if(ret == 0)
  58. {
  59. printf("监听成功\n");
  60. }
  61. else
  62. {
  63. perror("listen");
  64. return -1;
  65. }
  66. struct sockaddr_in client;
  67. int n = sizeof(client);
  68. int connfd;
  69. while(1)
  70. {
  71. connfd = accept(sockfd, (struct sockaddr *)&client, &n);
  72. if(connfd == -1)
  73. {
  74. perror("accept");
  75. return -1;
  76. }
  77. else
  78. {
  79. printf("连接成功\n");
  80. }
  81. printf("client ip:%s\nclient port:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
  82. pthread_t pid;
  83. pthread_create(&pid, NULL, send_data, (void *)&connfd);
  84. pthread_detach(pid);
  85. while(1)
  86. {
  87. char buf[64] = {0};
  88. ret = read(connfd, buf, 64);
  89. if(ret < 0)
  90. {
  91. perror("read");
  92. return -1;
  93. }
  94. else if(ret == 0)
  95. {
  96. close(connfd);
  97. break;
  98. }
  99. printf(">>:%s\n", buf);
  100. if(strcmp(buf, "time") == 0)
  101. {
  102. time_t now = time(NULL);
  103. char *p = ctime(&now);
  104. char data[64];
  105. strcpy(data, p);
  106. write(connfd, data, strlen(data));
  107. memset(buf, 0, 64);
  108. memset(data, 0, 64);
  109. }
  110. else if(strncmp(buf, "get", 3) == 0)
  111. {
  112. int i, j = 0;
  113. char str[64] = {0};
  114. for(i = 4; i <= strlen(buf)-1; i++)
  115. {
  116. str[j] = buf[i];
  117. j++;
  118. }
  119. memset(buf, 0, 64);
  120. int fd = open(str, O_RDONLY);
  121. while((ret = read(fd, buf, 64)) != 0)
  122. {
  123. write(connfd, buf, ret);
  124. memset(buf, 0, 64);
  125. }
  126. write(connfd, "over", 64);
  127. memset(buf, 0, 64);
  128. memset(str, 0, 64);
  129. close(fd);
  130. }
  131. else
  132. {
  133. char arr[64];
  134. sprintf(arr, "%ld", strlen(buf));
  135. write(connfd, arr, strlen(arr));
  136. memset(buf, 0, 64);
  137. memset(arr, 0, 64);
  138. }
  139. }
  140. }
  141. close(sockfd);
  142. return 0;
  143. }

 

 再来创建一个客户端:

  1. #include <strings.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include <sys/types.h> /* See NOTES */
  6. #include <sys/socket.h>
  7. #include <netinet/ip.h>
  8. #include <unistd.h>
  9. #include <arpa/inet.h>
  10. #include <pthread.h>
  11. void *recv_data(void *arg)
  12. {
  13. int sockfd = *(int *)arg;
  14. char s[64] = {0};
  15. while(1)
  16. {
  17. int ret = read(sockfd, s, 64);
  18. if(ret < 0)
  19. {
  20. perror("read");
  21. return NULL;
  22. }
  23. printf("%s\n", s);
  24. memset(s, 0, 64);
  25. }
  26. }
  27. int main(int argc, char *argv[])
  28. {
  29. int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  30. if(sockfd == -1)
  31. {
  32. perror("socket");
  33. return -1;
  34. }
  35. pthread_t pid;
  36. pthread_create(&pid, NULL, recv_data, (void *)&sockfd);
  37. pthread_detach(pid);
  38. struct sockaddr_in ser;
  39. ser.sin_family = AF_INET;
  40. ser.sin_port = htons(11111);
  41. //ser.sin_addr.s_addr = inet_addr("0.0.0.0");
  42. ser.sin_addr.s_addr = htonl(INADDR_ANY);
  43. int ret = connect(sockfd, (struct sockaddr *)&ser, sizeof(ser));
  44. if(ret == -1)
  45. {
  46. perror("connect");
  47. return -1;
  48. }
  49. char buf[64];
  50. while(1)
  51. {
  52. fgets(buf, 64, stdin);
  53. buf[strlen(buf)-1] = '\0';
  54. write(sockfd, buf, 64);
  55. if(strcmp(buf, "time") == 0)
  56. {
  57. char data[64];
  58. read(sockfd, data, 64);
  59. printf(">>:%s\n", data);
  60. memset(buf, 0, 64);
  61. memset(data, 0, 64);
  62. }
  63. else if(strncmp(buf, "get", 3) == 0)
  64. {
  65. char str[64];
  66. while(read(sockfd, str, 64) != 0)
  67. {
  68. if(strcmp(str, "over") == 0)
  69. {
  70. memset(str, 0, 64);
  71. break;
  72. }
  73. printf(">>:%s\n", str);
  74. memset(buf, 0, 64);
  75. memset(str, 0, 64);
  76. }
  77. }
  78. else
  79. { char arr[64] = {0};
  80. read(sockfd, arr, 64);
  81. printf(">>:%s\n", arr);
  82. memset(buf, 0, 64);
  83. memset(arr, 0, 64);
  84. }
  85. }
  86. close(sockfd);
  87. return 0;
  88. }

 运行结果如下:

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/144092
推荐阅读
相关标签
  

闽ICP备14008679号