【从零开始的嵌入式生活】网络编程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哦,需要自取,我尽量一天一更,大家和我一起变强呀!最后三连即可提高学习效率!!!


相关文章
|
7月前
|
网络协议 物联网
VB6网络通信软件上位机开发,TCP网络通信,读写数据并处理,完整源码下载
本文介绍使用VB6开发网络通信上位机客户端程序,涵盖Winsock控件的引入与使用,包括连接服务端、发送数据(如通过`Winsock1.SendData`方法)及接收数据(利用`Winsock1_DataArrival`事件)。代码实现TCP网络通信,可读写并处理16进制数据,适用于自动化和工业控制领域。提供完整源码下载,适合学习VB6网络程序开发。 下载链接:[完整源码](http://xzios.cn:86/WJGL/DownLoadDetial?Id=20)
244 12
|
7月前
|
机器学习/深度学习 API Python
Python 高级编程与实战:深入理解网络编程与异步IO
在前几篇文章中,我们探讨了 Python 的基础语法、面向对象编程、函数式编程、元编程、性能优化、调试技巧、数据科学、机器学习、Web 开发和 API 设计。本文将深入探讨 Python 在网络编程和异步IO中的应用,并通过实战项目帮助你掌握这些技术。
|
10月前
|
负载均衡 网络协议 算法
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
这网络层就像搭积木一样,上层协议都是基于下层协议搭出来的。不管是ping(用了ICMP协议)还是tcp本质上都是基于网络层IP协议的数据包,而到了物理层,都是二进制01串,都走网卡发出去了。 如果网络环境没发生变化,目的地又一样,那按道理说他们走的网络路径应该是一样的,什么情况下会不同呢? 我们就从路由这个话题聊起吧。
223 4
不为人知的网络编程(十九):能Ping通,TCP就一定能连接和通信吗?
|
11月前
|
监控 安全
公司上网监控:Mercury 在网络监控高级逻辑编程中的应用
在数字化办公环境中,公司对员工上网行为的监控至关重要。Mercury 作为一种强大的编程工具,展示了在公司上网监控领域的独特优势。本文介绍了使用 Mercury 实现网络连接监听、数据解析和日志记录的功能,帮助公司确保信息安全和工作效率。
214 51
|
9月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
469 15
|
8月前
|
网络协议 测试技术 Linux
Golang 实现轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库
gev 是一个基于 epoll 和 kqueue 实现的高性能事件循环库,适用于 Linux 和 macOS(Windows 暂不支持)。它支持多核多线程、动态扩容的 Ring Buffer 读写缓冲区、异步读写和 SO_REUSEPORT 端口重用。gev 使用少量 goroutine,监听连接并处理读写事件。性能测试显示其在不同配置下表现优异。安装命令:`go get -u github.com/Allenxuxu/gev`。
146 0
|
10月前
|
网络协议
TCP报文格式全解析:网络小白变高手的必读指南
本文深入解析TCP报文格式,涵盖源端口、目的端口、序号、确认序号、首部长度、标志字段、窗口大小、检验和、紧急指针及选项字段。每个字段的作用和意义详尽说明,帮助理解TCP协议如何确保可靠的数据传输,是互联网通信的基石。通过学习这些内容,读者可以更好地掌握TCP的工作原理及其在网络中的应用。
|
11月前
|
监控 网络协议 网络性能优化
网络通信的核心选择:TCP与UDP协议深度解析
在网络通信领域,TCP(传输控制协议)和UDP(用户数据报协议)是两种基础且截然不同的传输层协议。它们各自的特点和适用场景对于网络工程师和开发者来说至关重要。本文将深入探讨TCP和UDP的核心区别,并分析它们在实际应用中的选择依据。
278 3
|
11月前
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
196 13
|
11月前
|
数据库连接 Go 数据库
Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性
本文探讨了Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性;防御编程则强调在编码时考虑各种错误情况,确保程序健壮性。文章详细介绍了这两种技术在Go语言中的实现方法及其重要性,旨在提升软件质量和可靠性。
169 1

热门文章

最新文章