> 文档中心 > 【从零开始的嵌入式生活】网络编程2——TCP编程

【从零开始的嵌入式生活】网络编程2——TCP编程

【从零开始的嵌入式生活】网络编程2——TCP编程
今天正式开启网络编程,之前博主没怎么接触过网络编程,所以这部分会写的细一点,预计这部分是需要七天文章对应一星期的写作。希望有人愿意跟我一起学习呀。

🧑🏻作者简介:一个学嵌入式的年轻人
✨联系方式:2201891280(QQ)
📔源码地址:https://gitee.com/xingleigao/study_qianrushi
全文大约阅读时间: 60min


文章目录

  • TCP编程API
    • socket()函数 创建fd
    • bind()函数 绑定
      • 一个小demo
    • listen()函数 把主动套接字转变为被动套接字
    • accept() 阻塞等待客户端连接请求
    • connect() 客户端的连接函数
      • 最终demo
  • 写在最后

TCP编程API

下面是一个总览图。我们主要学的就是其中的五个主要的api。
【从零开始的嵌入式生活】网络编程2——TCP编程


socket()函数 创建fd

#include    /* See NOTES */#include int socket(int domain, int type, int protocol);

参数:

  1. domain
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
AF_UNIX、 AF_LOCAL Local communication unix(7)
AF_NETLINK Kernel user interface device netlink(7)
AF_PACKET Low-level packet interface packet(7)
  1. type
SOCK_STREAM 流式套接字,唯一对应于TCP
SOCK_DGRAM 数据包套接字,唯一对应着UDP
SOCK_RAW 原始套接字
  1. protocol:一般为0,原始套接字编程时需填充

返回值:

RETURN VALUE
On success, a file descriptor for the new socket is returned. On error, -1 is returned, and errnois set appropriately.

成功的时候会返回一个文件描述符。失败返回-1。


bind()函数 绑定

#include    /* See NOTES */#include int bind(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);

参数:
sockfp:通过socket()函数拿到的fd
addr:struct sockaddr的结构体变量的地址
addrlen:地址长度

addr的通用结构体

struct sockaddr { sa_family_t sa_family;//2字节 char sa_data[14];//14字节    }

基于Internet通信的结构体

struct sockaddr_in {     sa_family_t    sin_family; /* address family: AF_INET */ //2字节     in_port_t      sin_port;   /* port in network byte order */ //2字节      struct in_addr sin_addr;   /* internet address */ //4字节 };/* Internet address. */struct in_addr {     uint32_ts_addr;     /* address in network byte order */ };

【从零开始的嵌入式生活】网络编程2——TCP编程
其中sin_zero 必须填充为0!

返回值:

On success, zero is returned. On error, -1 is returned, and errno is set appropriately.


一个小demo

 int fd = -1; struct sockaddr_in sin; /*创建sockt fd*/ if((fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0){ perror("socket");  exit(1); } /*2.绑定 */ /*2.1填充struct sockaddr_in 结构体变量*/ bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); //网络字节序的端口号转换 //sin.sin_addr = inet_addr(SERV_IP_ADDR); //IPV4 if(inet_pton(AF_INET, SERV_IP_ADDR,(void *)&sin.sin_addr) != 1){  perror("inet_pton");  exit(1); } /*2.2绑定*/ if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0){  perror("bind");  exit(1); }; /*3.调用listen()把主动套接字转变为被动套接字*/ if(listen(fd, BACKLOG) < 0){  perror("listen");  exit(1); }

如果是IPV6的编程可以man 7 ipv6,通常更通用的方法通过struct sockaddr_storage来编程。因为长度问题


listen()函数 把主动套接字转变为被动套接字

#include    /* See NOTES */#include int listen(int sockfd, int backlog);

参数:

  • sockfd : 通过socket()拿到的fd
  • backlog : 一般填5(同时允许几路客户端和服务器进行正在连接的过程,测试得知,ARM最大值为8)

内核中服务器套接字fd会维护2个链表

  1. 正在三次握手的客户端链表(数量= 2*backlog +1)
  2. 已经建立好连接的客户端链表(已经三次握手分配好了newfd)

返回值: On success, zero is returned. On error, -1 is returned, and errno is set appropriately.

accept() 阻塞等待客户端连接请求

#include    /* See NOTES */#include int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:

  • sockfd:经过前面socket()创建的fd
  • addr和addrlen:获取到客户端的ip地址和端口号
    返回值:

On success, these system calls return a nonnegative integer that is a file descriptor for the ac‐cepted socket. On error, -1 is returned, errno is set appropriately, and addrlen is left un‐changed.

成功时返回已经建立好连接的新的newid

connect() 客户端的连接函数

#include    /* See NOTES */#include int connect(int sockfd, const struct sockaddr *addr,     socklen_t addrlen);

connect()函数和bind()函数类似

最终demo

Clent:

#include "net.h"#define QUIT_STR "quit"int main(void){ int fd = -1; struct sockaddr_in sin; /*创建sockt fd*/ if((fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0){ perror("socket");  exit(1); } /*2.连接服务器*/ bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); //网络字节序的端口号转换 //sin.sin_addr = inet_addr(SERV_IP_ADDR); //IPV4 if(inet_pton(AF_INET, SERV_IP_ADDR,(void *)&sin.sin_addr) != 1){  perror("inet_pton");  exit(1); } if(connect(fd, (struct sockaddr *)&sin, sizeof(sin)) <0){  perror("connect");  exit(1); } /*3.读写文件*/ char buf[BUFSIZ]; while(1){  bzero(buf, BUFSIZ);  if(fgets(buf, BUFSIZ - 1, stdin) == NULL){   continue;  }  write(fd, buf, strlen(buf));  if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){   printf("Client is exiting!\n");   break;  } } /*4.关闭服务器*/ close(fd); return 0;}

Server:

#include "net.h"int main(void){ int fd = -1; struct sockaddr_in sin; /*创建sockt fd*/ if((fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0){ perror("socket");  exit(1); } /*2.绑定 */ /*2.1填充struct sockaddr_in 结构体变量*/ bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); //网络字节序的端口号转换 //sin.sin_addr = inet_addr(SERV_IP_ADDR); //IPV4 if(inet_pton(AF_INET, SERV_IP_ADDR,(void *)&sin.sin_addr) != 1){  perror("inet_pton");  exit(1); } /*2.2绑定*/ if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0){  perror("bind");  exit(1); }; /*3.调用listen()把主动套接字转变为被动套接字*/ if(listen(fd, BACKLOG) < 0){  perror("listen");  exit(1); } int newfd = -1; /*4.阻塞等待客户端连接请求 */ newfd = accept(fd, NULL, NULL); if(newfd < 0){  perror("accept");  exit(1); } /*5.读写*/ //和newfd进行数据读写 int ret = -1; char buf[BUFSIZ]; while(1){  bzero(buf, BUFSIZ);  do{   ret = read(newfd, buf, BUFSIZ);  }while(ret < 0 && EINTR == errno);  if(ret < 0){   perror("read");   exit(1);  }  if(!ret){//对方关闭   break;  }  printf("Receive data : %s\n",buf);  if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){ //用户退出  printf("Client is exiting!\n");   break;  } } close(newfd); return 0;}

net.h

#ifndef __NET_H__#define __NET_H__#include #include #include #include #include #include #include #include #include #include #include #define SERV_PORT 5001#define SERV_IP_ADDR "127.0.0.1"#define BACKLOG 5#define QUIT_STR "quit"#endif

写在最后

今天开始网络编程,这部分非常重要,所有文件我都放在了gitee哦,需要自取,我尽量一天一更,大家和我一起变强呀!最后三连即可提高学习效率!!!