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

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

文章目录

TCP编程API

socket()函数 创建fd

bind()函数 绑定

一个小demo

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

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

connect() 客户端的连接函数

最终demo

写在最后

TCP编程API

下面是一个总览图。我们主要学的就是其中的五个主要的api。


socket()函数 创建fd

 

#include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int socket(int domain, int type, int protocol);


参数:


1.domain

faad2d63665697a22bb21cb9a6c1ddb.png

2.type

9a27f1b92cbc274eaf501dcc0ef7879.png


3。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哦,需要自取,我尽量一天一更,大家和我一起变强呀!最后三连即可提高学习效率!!!


相关文章
|
1月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
95 15
|
2月前
|
负载均衡 网络协议 算法
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
这网络层就像搭积木一样,上层协议都是基于下层协议搭出来的。不管是ping(用了ICMP协议)还是tcp本质上都是基于网络层IP协议的数据包,而到了物理层,都是二进制01串,都走网卡发出去了。 如果网络环境没发生变化,目的地又一样,那按道理说他们走的网络路径应该是一样的,什么情况下会不同呢? 我们就从路由这个话题聊起吧。
78 4
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
|
3月前
|
监控 安全
公司上网监控:Mercury 在网络监控高级逻辑编程中的应用
在数字化办公环境中,公司对员工上网行为的监控至关重要。Mercury 作为一种强大的编程工具,展示了在公司上网监控领域的独特优势。本文介绍了使用 Mercury 实现网络连接监听、数据解析和日志记录的功能,帮助公司确保信息安全和工作效率。
123 51
|
2月前
|
网络协议
TCP报文格式全解析:网络小白变高手的必读指南
本文深入解析TCP报文格式,涵盖源端口、目的端口、序号、确认序号、首部长度、标志字段、窗口大小、检验和、紧急指针及选项字段。每个字段的作用和意义详尽说明,帮助理解TCP协议如何确保可靠的数据传输,是互联网通信的基石。通过学习这些内容,读者可以更好地掌握TCP的工作原理及其在网络中的应用。
|
3月前
|
监控 网络协议 网络性能优化
网络通信的核心选择:TCP与UDP协议深度解析
在网络通信领域,TCP(传输控制协议)和UDP(用户数据报协议)是两种基础且截然不同的传输层协议。它们各自的特点和适用场景对于网络工程师和开发者来说至关重要。本文将深入探讨TCP和UDP的核心区别,并分析它们在实际应用中的选择依据。
102 3
|
3月前
|
数据库连接 Go 数据库
Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性
本文探讨了Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性;防御编程则强调在编码时考虑各种错误情况,确保程序健壮性。文章详细介绍了这两种技术在Go语言中的实现方法及其重要性,旨在提升软件质量和可靠性。
57 1
|
4月前
|
Web App开发 缓存 网络协议
不为人知的网络编程(十八):UDP比TCP高效?还真不一定!
熟悉网络编程的(尤其搞实时音视频聊天技术的)同学们都有个约定俗成的主观论调,一提起UDP和TCP,马上想到的是UDP没有TCP可靠,但UDP肯定比TCP高效。说到UDP比TCP高效,理由是什么呢?事实真是这样吗?跟着本文咱们一探究竟!
122 10
|
3月前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
4月前
|
网络协议 Java API
【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法
【网络】TCP回显服务器和客户端的构造,以及相关bug解决方法
90 2
|
4月前
|
存储 网络协议 Java
【网络】UDP和TCP之间的差别和回显服务器
【网络】UDP和TCP之间的差别和回显服务器
109 1

热门文章

最新文章