【从零开始的嵌入式生活】网络编程2——TCP编程
今天正式开启网络编程,之前博主没怎么接触过网络编程,所以这部分会写的细一点,预计这部分是需要七天文章对应一星期的写作。希望有人愿意跟我一起学习呀。文章目录TCP编程APIsocket()函数 创建fdbind()函数 绑定一个小demolisten()函数 把主动套接字转变为被动套接字accept() 阻塞等待客户端连接请求connect() 客户端的连接函数最终demo写在最后TCP编程API下

今天正式开启网络编程,之前博主没怎么接触过网络编程,所以这部分会写的细一点,预计这部分是需要七天文章对应一星期的写作。希望有人愿意跟我一起学习呀。
🧑🏻作者简介:一个学嵌入式的年轻人
✨联系方式:2201891280(QQ)
📔源码地址:https://gitee.com/xingleigao/study_qianrushi
⏳全文大约阅读时间: 60min
文章目录
TCP编程API
下面是一个总览图。我们主要学的就是其中的五个主要的api。
socket()函数 创建fd
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
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 <sys/types.h> /* See NOTES */
#include <sys/socket.h>
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_t s_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 <sys/types.h> /* See NOTES */
#include <sys/socket.h>
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 <sys/types.h> /* See NOTES */
#include <sys/socket.h>
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 <sys/types.h> /* See NOTES */
#include <sys/socket.h>
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 <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#define SERV_PORT 5001
#define SERV_IP_ADDR "127.0.0.1"
#define BACKLOG 5
#define QUIT_STR "quit"
#endif
写在最后
今天开始网络编程,这部分非常重要,所有文件我都放在了gitee哦,需要自取,我尽量一天一更,大家和我一起变强呀!最后三连即可提高学习效率!!!
更多推荐



所有评论(0)