【北京迅为】iTOP-4412全能版使用手册-第三十二章 网络通信-TCP套字节

iTOP-4412全能版采用四核Cortex-A9,主频为1.4GHz-1.6GHz,配备S5M8767 电源管理,集成USB HUB,选用高品质板对板连接器稳定可靠,大厂生产,做工精良。接口一应俱全,开发更简单,搭载全网通4G、支持WIFI、蓝牙、陀螺仪、CAN总线、RS485总线、500万摄像头等模块,稳定运行Android 4.0.3/Android 4.4操作,系统通用Linux-3.0.15+Qt操作系统(QT支持5.7版本),Ubuntu版本:12.04,接口智能分配 方便好用。


第三十二章 网络通信-TCP套字节

在 Linux 下,网络资源非常丰富,底层的东西不需要去管,只需要知道怎么实现最终需要的功能即可。

本章配套视频为:

"视频 18 网络通信-套字节 TCP"

32.1 TCP通信简介

本节来学习一下 linux 网络通信程序的编写,使用的硬件平台是 iTOP-4412 开发板。TCP 是一种面向连接的、可靠的、基于 IP 的传输层协议。通过 TCP 可以保证传送的数据的正确性。

Linux 下网络通信程序基本上都是采用 socket 的方式。socket 起源于 Unix,而Unix/Linux 基本哲学之一就是"一切皆文件",都可以用"打开 open->读写 read/write-> 关闭 close"模式来操作。Socket 就是该模式的一个实现,socket 即是一种特殊的文件,一些 socket 函数就是对其进行的操作(读/写 IO、打开、关闭)。说白了 socket 是应用程序与TCP/IP 协议族通信的中间软件抽象层,它是一组接口。

现在看一下基于 TCP/IP 应用程序通信的流程,如下图。

通过上图可以看到 TCP/IP 通信是基于服务器/客户端的模式来实现的,首先是服务器(server)端调用 socket 函数创建一个套接字,然后调用 bind 绑定函数,绑定函数主要是设置通信时使用哪种地址族(IPv4,IPv6 等),使用的端口号。然后调用 listen 函数来监听客户端的连接请求。

现在来看下客户端(client)端的流程,首先调用 socket 函数创建一个套接字,然后调用 connect 函数连接服务器,这时服务器端的 listen 函数监听到客户端的连接请求就会调用accept 函数去接受请求,这样连接就建立好了。之后双方就可以调用 read/write 函数收发数据了,在完成通信以后服务器(server)和客户端(client)调用 close 函数关闭创建的套接字。

32.2 服务器程序

下面来看一个实现 TCP/IP 的通信的例子,首先来看一下服务器(server)端的代码:

复制代码
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main()
{
 	int sfp, nfp, num = 0;
 	struct sockaddr_in s_add,c_add;
 	int sin_size;
 	unsigned short portnum=0x8888;
 	char buffer[100] = {0};
 	printf("Hello,welcome to my server !\r\n");

 	/* 创建 TCP 连接的套接字 */
 	sfp = socket(AF_INET, SOCK_STREAM, 0);
 	if(-1 == sfp)
 	{
 		printf("socket fail ! \r\n");
 		return -1;
 	}
 	printf("socket ok !\r\n");

 	/* 变量 s_add 清零 */
 	bzero(&s_add,sizeof(struct sockaddr_in));
 	s_add.sin_family=AF_INET;
 	s_add.sin_addr.s_addr=htonl(INADDR_ANY);
 	s_add.sin_port=htons(portnum);

 	/* 绑定 s_add 到套接字 sfp 上 */
 	if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
 	{
 		printf("bind fail !\r\n");
 		return -1;
 	}
 	printf("bind ok !\r\n");
 	/*监听函数,静听客户端的连接请求 */
 	if(-1 == listen(sfp,5))
 	{
 		printf("listen fail !\r\n");
 		return -1;
 	}
 	printf("listen ok\r\n")
 	sin_size = sizeof(struct sockaddr_in);

 	/* 接受连接请求 */
 	nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
 	if(-1 == nfp)
 	{
 		printf("accept fail !\r\n");
 		return -1;
 	}

 	printf("accept ok!\r\nServer start get connect from %#x : %#x\r\n", ntohl(c_add.sin_addr.s_addr), ntohs(c_add.sin_port));
 	while(1)
 	{
 		memset(buffer, 0, 100);
 		sprintf(buffer, "hello,welcome to my server(%d) \r\n", num++);

 		/* 发送函数 */
 		send(nfp, buffer, strlen(buffer), 0);
 		usleep(500000);

 	}


 	/* 关闭 socket 连接 */
 	close(nfp);

 	/* 关闭 socket 连接 */
 	close(sfp);

 	return 0;
}

程序首先是包含一些需要用到的头文件,然后是 main 主函数,在 main 函数里面首先是定义了一些变量,然后调用 socket 函数创建一个套接字,socket 函数的第二个参数是SOCK_STREAM,表示创建的是 TCP 连接。然后调用 bzero 函数把变量 s_add 清零,然后给s_add 结构里面的变量赋值:

s_add.sin_family=AF_INET;//使用 IPv4 协议

s_add.sin_addr.s_addr=htonl(INADDR_ANY);//允许任何地址

s_add.sin_port=htons(portnum);//设置端口号

然后调用 bind 绑定函数,使用的是 IPv4 协议族,然后调用 listen 监听函数,监听用户

的连接请求。在监听到用户的请求后调用 accept 函数接受请求,然后进入到循环发送的代码,会循环发送"hello,welcome to my server"+发送次数号,最后会调用 close 关闭套接字。

32.3 客户端程序

下面来看看客户端(client)端的代码:

复制代码
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(int argc, char **argv)
{
 	int cfd;
 	int recbyte;
 	int sin_size;
 	char buffer[1024] = {0};
 	struct sockaddr_in s_add, c_add;
 	unsigned short portnum = 0x8888;

 	printf("Hello,welcome to client!\r\n");

 	if(argc != 2)
 	{
 		printf("usage: echo ip\n");
 		return -1;
 	}

 	/* 创建一个 TCP 连接的 socket */
 	cfd = socket(AF_INET, SOCK_STREAM, 0);
 	if(-1 == cfd)
 	{
 		printf("socket fail ! \r\n");
 		return -1;
 	}
 	printf("socket ok !\r\n");


 	/* 变量 s_add 清零 */
 	bzero(&s_add,sizeof(struct sockaddr_in));
 	s_add.sin_family=AF_INET;
 	s_add.sin_addr.s_addr= inet_addr(argv[1]);
 	s_add.sin_port=htons(portnum);
 	printf("s_addr = %#x ,port : %#x\r\n",s_add.sin_addr.s_addr,s_add.sin_port);


 	/* 连接服务器函数 */
 	if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))

 	{

 		printf("connect fail !\r\n");
 		return -1;
 	}


 	printf("connect ok !\r\n");


 	while(1)

 	{

 		/* 接收服务器发过来的数据 */
 		if(-1 == (recbyte = read(cfd, buffer, 1024)))
 		{
 			printf("read data fail !\r\n");
 			return -1;
 		}

 		printf("read ok\r\nREC:\r\n");
 		buffer[recbyte]='\0';
 		printf("%s\r\n",buffer);
 	}

 	/* 关闭套接字 */
 	close(cfd);

 	return 0;
}

首先是包含一些需要的头文件,然后进入 main 主函数定义了一些变量,然后调用socket 函数创建套接字,然后调用 bzero 函数把变量 s_add 清零,然后给 s_add 结构里面的变量赋值:

s_add.sin_family=AF_INET;//使用 IPv4 协议

s_add.sin_addr.s_addr= inet_addr(argv1);//设置要连接的 IP 地址(这里是执行程序的时候传递进来的)

s_add.sin_port=htons(portnum);//设置端口号

然后调用 connect 函数来连接服务器(server),在连接成功后,就进入了循环接收函数,使用 read 函数接收服务器发送的数据。最后会调用 close 函数关闭套接字。

32.4 编译测试

下面来编译下这两个程序,服务器(server)的程序运行在虚拟机 Ubuntu 上,所以使用下面的命令编译:

gcc -o server server.c

这样就生成了 server 可执行文件,客户端(client)的程序运行在 iTOP-4412 开发板上,使用下面的命令编译:

arm-none-linux-gnueabi-gcc -o client client.c

这样就生成了 client 可执行程序,把 client 下载到 iTOP-4412 开发板上,现在开始运行这两个程序,首先在虚拟机 Ubuntu 上运行 serevr 程序,如下图。

可以看到 server 打印出来的运行信息,现在 server 运行到了 listen 函数开始监听客户端的连接。下面在 iTOP-4412 开发板上运行 client 程序(因为我把 client 下载到了/bin 目录下,所以先进入到/bin 目录)执行下面的命令:

./client 192.168.1.77

上面命令里面的 192.168.1.77 是虚拟机 Ubuntu 的 IP 地址,看到程序连接成功,首先看一下虚拟机 Ubuntu 上的 server 打出的信息,如下图。

可以看到上图中 server 打印出了客户端的 ip 地址和端口号"Server start get connect from 0xc0a801e6 : 0xe171"。

然后看一下 iTOP-4412 开发板串口的打印信息,如下图。

通过上图可以看到打印连接成功"connect ok !",然后串口会一直打印

read ok REC:

hello,welcome to my server(0)

至此,基于 TCP/IP 的 socket 网络编程就已经完成了。

相关推荐
且从容.2 小时前
Ubuntu安装flume
linux·ubuntu·flume
承渊政道2 小时前
Linux网络学习【Reactor反应堆模式】
linux·网络·学习·ubuntu·reactor
不脱发的程序猿3 小时前
GD32串口数据收进内存之后DMA如何处理?
单片机·嵌入式硬件·dma·嵌入式·串口通信·gd32
且从容.5 小时前
ubuntu安装flink
linux·ubuntu·flink
爱和冰阔落7 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
程序员沉梦听雨7 小时前
WSL2的安装与使用
linux·wsl
微小的鱼7 小时前
Linux的渲染与显示系统链路
android·java·linux
FPGA小迷弟7 小时前
FPGA 与千兆以太网校验和:从原理到 Verilog 实现
嵌入式硬件·fpga开发·无人机·ai编程·fpga
lisanmengmeng7 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
Android系统攻城狮8 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频