【从零开始的嵌入式生活】网络编程2——TCP编程
今天正式开启网络编程,之前博主没怎么接触过网络编程,所以这部分会写的细一点,预计这部分是需要七天文章对应一星期的写作。希望有人愿意跟我一起学习呀。
🧑🏻作者简介:一个学嵌入式的年轻人
✨联系方式:2201891280(QQ)
📔源码地址:https://gitee.com/xingleigao/study_qianrushi
⏳全文大约阅读时间: 60min
文章目录
- TCP编程API
- 写在最后
TCP编程API
下面是一个总览图。我们主要学的就是其中的五个主要的api。
socket()函数 创建fd
#include /* See NOTES */#include int socket(int domain, int type, int protocol);
参数:
- 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) |
- type
SOCK_STREAM | 流式套接字,唯一对应于TCP |
SOCK_DGRAM | 数据包套接字,唯一对应着UDP |
SOCK_RAW | 原始套接字 |
- 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 */ };
其中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个链表
- 正在三次握手的客户端链表(数量= 2*backlog +1)
- 已经建立好连接的客户端链表(已经三次握手分配好了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哦,需要自取,我尽量一天一更,大家和我一起变强呀!最后三连即可提高学习效率!!!