>  기사  >  시스템 튜토리얼  >  임베디드 Linux 시리즈 8부: 네트워크 포트 작동

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

WBOY
WBOY앞으로
2024-02-05 12:15:27553검색

1. 소개

비교적 고성능인 일부 마이크로 컨트롤러에는 이더넷 인터페이스가 있는 경우가 많습니다. 네트워크 포트는 네트워크 프로토콜 스택의 작동과 관련되어 있기 때문에 MCU에서 비교적 복잡한 주변 장치로 간주됩니다. 일반적으로 네트워크 프로토콜 스택은 RTOS(실시간 운영 체제)에서 실행되므로 일반 마이크로 컨트롤러 개발자가 네트워크 포트를 사용하기 어렵습니다. Linux에서는 네트워크 포트가 자주 사용되는 인터페이스입니다. Linux는 성숙하고 완전한 네트워크 통신 프로토콜 스택을 갖고 있고 기본 드라이버는 제조업체에서 제공하므로 상대적으로 사용하기 편리합니다. 이 기사는 모든 사람에게 도움이 되기를 바라며 Linux에서 네트워크 포트의 사용을 간략하게 요약합니다.

2 환경 소개

2.1.하드웨어

1) 인터넷의 제3자가 만든 NUC972 개발 보드:

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

구매에 관심이 있는 친구는 타오바오 매장에서 구매할 수 있습니다:

https://s.click.taobao.com/X8mza8w

이 글에서는 주로 보드의 네트워크 포트에 대해 다룹니다.

2) USB-RS232 케이블 1개, 네트워크 케이블 1개, 전원 코드 1개, 마이크로 USB 케이블 1개

2.2.소프트웨어

1) 이전 글의 Uboot와 Kernel을 계속해서 사용하고 있습니다.

2) Buildroot를 사용하여 Rootfs를 재생성합니다. NUC972 Buildroot의 다운로드 주소는 https://github.com/OpenNuvoton/NUC970_Buildroot입니다. 여기서 Buildroot를 사용하여 Rootfs를 재생성하는 이유는 Buildroot 도구를 사용하여 우리가 원하는 것을 추가하기 위해서입니다. 이 기사에서 필요한 ssh 기능은 매우 편리할 것이며 수동으로 이식하는 것보다 훨씬 쉽습니다. 어쩌면 이해가 안 되실 수도 있습니다. 관심이 있으시면 온라인 튜토리얼을 참조하여 dropbear를 수동으로 이식하여 ssh 기능을 구현해 보시면 더 깊이 이해하실 수 있을 것입니다.

3) 크로스 툴 체인 arm_linux_4.8.tar.gz는 이전 기사에서도 계속 사용되는 것으로 추측됩니다. 이 툴 체인도 Buildroot에 의해 생성된 것 같습니다.

3Buildroot는 Rootfs

를 만듭니다.

여기에서는 자세한 단계를 더 이상 소개하지 않습니다. "Buildroot를 사용하여 I.MX6용 루트 파일 시스템 만들기" 이전에 게시한 기사를 참조하세요. 여기서 몇 가지 사항을 설명하겠습니다.

1) 공식 Buildroot를 다운로드한 후 해당 디렉터리에 들어가서 다음 지침을 실행합니다.

make nuvoton_nuc972_defconfig

만들다

첫 번째 편집 시간은 약간 길며 온라인에서 많은 파일을 다운로드하므로 모두가 인내심을 가져야 합니다.

2) 크로스 툴체인 문제와 관련하여 Buildroot 툴체인이 사용됩니다. 이 Buildroot를 선택하면 툴체인이 처음부터 생성됩니다. 컴파일이 완료되면 출력/호스트/디렉토리에 새로 만들어진 툴체인이 생성되는 것을 볼 수 있습니다. 저는 개인적으로 공식 툴체인이 여기에서 나온 것이라고 추측합니다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동3) Dropbear는 기본 구성에 선택되어 있지 않으므로 직접 선택할 수 있습니다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동4) 컴파일이 완료된 후 생성된 rootfs는 output/images/rootfs.tar입니다. 이를 NUC972 보드에 프로그래밍하려면 먼저 압축을 푼 다음 mkyaffs2를 사용하여 . img 형식 파일입니다.

5) Uboot, Kernel, Rootfs 등을 보드에 다시 다운로드하고 dropbear 및 네트워크 포트를 구성한 후 passwd 명령을 사용하여 루트 사용자에 대한 비밀번호를 설정하면 장점이 있습니다. 누구든지 시스템에 직접 로그인하는 것을 방지할 수 있습니다.

보드와 컴퓨터에 네트워크 케이블을 연결하고 컴퓨터 IP를 192.168.0.50으로 설정한 후 직렬 포트 로그인 인터페이스에 ifconfig eth0 192.168.0.100을 입력하면 부팅 후 네트워크를 사용할 수 있는지 확인하려면 다음 문장을 추가하세요. /etc/init.d/ rcS 파일 끝. 이렇게 하면 나중에 직렬 포트를 연결할 필요가 없으며 네트워크 포트만 사용하여 Linux 시스템에 로그인할 수 있습니다. 동시에 파일을 보드에 복사할 필요도 없습니다. 이전과 마찬가지로 U 디스크를 통해 효율성이 크게 향상됩니다.

4개의 네트워크 포트 작동

4.1.관련 명령어

네트워크와 관련된 일반적인 명령으로는 앞서 네트워크 카드를 구성할 때 사용했던 ifconfig와 네트워크에 접근할 수 있는지 테스트하는 데 사용하는 ping 등이 있는데, 그 외 나중에 소개할 Route, ethtool 등이 있습니다. 실제로 사용됩니다.

4.2.C언어

Udp 및 tcp 통신은 가장 일반적으로 사용됩니다. 이에 대한 기본 소개는 여기에서 자세히 설명하지 않습니다. 이에 대해 잘 모르는 학생들은 Baidu에서 두 개의 기사를 읽으면 됩니다. 여기서는 UDP를 예로 들어 매우 전형적인 예를 살펴보겠습니다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

구현될 기능은 다음과 같습니다:

1) 클라이언트가 수동으로 입력한 데이터를 받습니다

2) 클라이언트는 위의 데이터를 서버로 보냅니다

3) 서버는 수신된 데이터를 클라이언트에게 다시 보냅니다

코드로 바로 이동:

으아악

먼저 gcc를 사용하여 Ubuntu에서 컴파일합니다. arm-linux-gcc를 먼저 실행한 다음 클라이언트를 실행하면 위에서 언급한 기능을 볼 수 있습니다. 우리는 원한다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

위 코드를 주의 깊게 살펴보세요. 설명이 필요한 몇 가지 사항이 있습니다.

1) UDP는 요청 연결 및 수락 과정이 없다는 점에서 TCP와 다르므로 실제로는 서버와 클라이언트 사이에 명확한 구분이 없습니다. 위의 서버와 클라이언트 명명은 단지 설명의 편의를 위한 것입니다. 내가 이해하는 방법: 데이터를 먼저 보내고(클라이언트는 데이터를 요청한 후 데이터를 받습니다), 서버는 데이터를 먼저 받은 다음 데이터를 보냅니다.

2) 서버 예시에서는 바인드 함수가 호출되지만 클라이언트 예시에서는 호출되지 않는 것을 보셨나요? 이유는 무엇인가요? 그 이유는 서버가 먼저 데이터를 받아야 작동하기 때문입니다. 포트가 바인딩되어 있지 않으면 어디서 데이터를 받을지 알 수 없습니다. Client가 Bind를 할 필요가 없는 이유는 먼저 전송을 하고, 전송 후 바로 전송 포트에서 데이터를 수신할 수 있기 때문입니다.

3) 실제 작업을 하다 보면 저를 포함해 많은 사람들이 포트에 대해 헷갈려 하는 경우가 많다는 것을 발견했습니다. 여기서 요약하면 UDP를 수신할 때 이 포트에서 데이터를 수신하기 전에 포트 번호(이 포트가 소유한 장치의 포트)를 바인딩해야 합니다. 데이터를 수신한 후 상대방의 IP 주소와 송신 포트 번호를 얻게 됩니다. . 전송 시 상대방의 IP와 포트를 지정하기만 하면 됩니다. 이 기기의 전송 포트는 무작위로 할당되므로 포트를 바인딩할 필요가 없습니다.

포트를 바인딩하지 않고 전송된 포트가 무작위로 할당되었는지 확인하기 위해 또 다른 작은 실험을 수행할 수 있습니다. 클라이언트를 껐다가 다시 열면 두 번 출력되는 포트 정보를 볼 수 있습니다. 포트 번호가 다릅니다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

4) 소켓을 생성하기 위해 소켓을 호출할 때 함수의 두 번째 매개변수에 SOCK_DGRAM이 전달되어 UDP 프로토콜이 사용됨을 나타냅니다. TCP인 경우 매개변수는 SOCK_STREAM입니다.

5) addr_local 멤버 변수에 값을 할당할 때 htonl(INADDR_ANY)을 사용하여 자동으로 IP 주소를 얻습니다.

INADDR_ANY를 사용하면 소프트웨어가 다른 호스트에서 실행되거나 호스트 IP 주소가 변경될 때 소스 코드를 변경하고 다시 컴파일할 필요가 없으며 소프트웨어 시작 시 수동으로 입력할 필요가 없다는 장점이 있습니다. 또한, 하나의 호스트에 여러 개의 IP 주소가 할당되어 있는 경우, 포트 번호가 일치하면 서로 다른 IP 주소에서 데이터를 수신할 수 있습니다.

6) 클라이언트에서 보낼 때 설정한 IP는 127.0.0.1입니다. 이것은 특수 IP 주소입니다. ifconfig를 사용하면 Ubuntu와 보드에서 볼 수 있습니다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동인터넷에서 영어 설명을 찾았습니다:

127.0.0.1은 "localhost"라고도 하는 루프백 인터넷 프로토콜(IP) 주소입니다. 이 주소는 최종 사용자가 사용하는 동일한 시스템이나 컴퓨터에 대한 IP 연결을 설정하는 데 사용됩니다. as는 로컬 시스템 자체를 나타냅니다.

다음 단계에서는 클라이언트 코드를 크로스 컴파일하고 보드에 올려 실행해보겠습니다.

첫 번째 addr_dest.sin_addr.s_addr=inet_addr(“127.0.0.1”)은 다음과 같이 변경됩니다.

addr_dest.sin_addr.s_addr=inet_addr(“192.168.0.50”);

192.168.0.50은 PC의 IP 주소입니다.

두 번째 while에 있는 세 문장(1)

문자 버프[1024] = {0x00};

printf(“문자열을 입력하세요: “);

fgets(버프,1024,stdin);

다음으로 변경됨:

char buff[1024] = “안녕 탑세믹 친구들!”;

//printf(“문자열을 입력하세요: “);

//fgets(buff,1024,stdin);

사용자가 정보를 입력할 때까지 기다리지 않고 클라이언트가 자동으로 데이터를 보내고 받을 수 있도록 하는 것이 목적입니다.

Ubuntu에서는 scp 명령을 사용하여 파일을 보드의 /opt 디렉터리에 직접 넣습니다

scp udp_client root@192.168.0.100:/opt

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

또한 Ubuntu에서 ssh 명령을 통해 Linux 시스템에 직접 로그인합니다

ssh root@192.168.0.100:/opt

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

종료하려면 그냥 종료를 입력하여 Ubuntu 명령줄 창으로 돌아가세요.

이렇게 하면 보드에 로그인하고 보드에 파일을 업로드하는 과정이 이전 Windows 직렬 포트 로그인 및 U 디스크 파일 전송에 비해 훨씬 편리해졌습니다.

우분투에서 udp_server를 실행했을 때 정말 기뻤습니다. 우분투에서 ssh로 보드에 로그인해서 udp_client를 실행해보니, 정상적으로 실행될 줄 알았는데, 실제로는 전혀 결과가 출력되지 않았습니다.

하지만 분명히 가상 머신 Ubuntu는 보드에 로그인하여 성공적으로 ping할 수 있고 보드도 IP 192.168.0.50으로 ping할 수 있는데 왜 udp가 통과할 수 없습니까? 나중에 고민 끝에 문제가 해결되었습니다. 해결책은 다음과 같습니다:

가상 머신의 기본 네트워크 설정 모드는 아래 표시된 NAT 모드입니다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

아래와 같이 브리지 모드로 수정했습니다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

그런 다음 네트워크 케이블을 뽑았다가 다시 연결하고 Ubuntu 가상 머신에서 네트워크 구성을 수정하세요

임베디드 Linux 시리즈 8부: 네트워크 포트 작동임베디드 Linux 시리즈 8부: 네트워크 포트 작동

가상 머신의 유선 연결을 수동으로 구성된 고정 IP, 192.168.0.xx 네트워크 세그먼트로 변경합니다(Windows 및 보드 IP와 충돌하지 않음). ifconfig를 사용하여 설정이 성공했는지 확인할 수 있습니다

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

이때, 게시판에 로그인 후 192.168.0.80으로 ping을 하시면 정상적으로 ping이 가능합니다. 이전에는 192.168.0.50으로 ping했는데, 이는 Windows 호스트의 IP입니다. 연결이 가능하다고 해서 가상머신에 연결이 가능하다는 의미는 아닙니다.

마지막으로 위 코드에서 IP를 변경합니다.

addr_dest.sin_addr.s_addr=inet_addr(“192.168.0.80”);

다시 컴파일하고, 다운로드하고, 한 번 실행하면 정상적으로 작동합니다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

추가 사항: 보드를 디버깅할 때 Windows에서 네트워크 디버깅 도우미를 자주 사용합니다. 이 도구를 사용하려면 프로토콜 유형, 로컬 호스트 주소, 로컬 호스트 포트 및 원격 호스트만 올바르게 구성한 다음 전송하면 됩니다. 하고 그 결과를 볼 수 있습니다.

예를 들어 Windows의 네트워크 디버깅 도우미를 활성화하여 다음과 같이 클라이언트와 가상 머신 서버 간의 통신을 시뮬레이션할 수도 있습니다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

5 실무 요약

실제 업무에서 흔히 저지르기 쉬운 실수를 예로 들어보세요.

프로세서가 UDP 통신 방법을 사용하여 네트워크 포트를 통해 외부 장치와 통신한다고 가정해 보겠습니다. 일반적인 작업 흐름은 다음과 같습니다. 먼저 데이터를 보낸 다음 외부 장치가 응답합니다.

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

이 모델은 위에서 언급한 서버 및 클라이언트 모델과 매우 유사합니다. 구현하려는 것은 클라이언트입니다. 즉, 먼저 보내기 위해 sendto 함수를 호출하고, 수신하기 위해 recvfrom 함수를 호출합니다. 정상적인 상황에서는 이와 같은 프로그램을 작성하는데 문제가 없지만 실제로는 외부 장치의 전원이 갑자기 꺼졌다가 켜지거나 정상 작동 중에 다시 시작되는 등의 비정상적인 상황을 많이 고려해야 합니다(그러나 CPU 장치에는 전원이 공급되지 않습니다). off) 어떤 문제가 발생할까요? 외부 장치의 전원이 꺼졌기 때문에 데이터를 수신할 수 없기 때문에 recvfrom 함수가 차단됩니다. 외부 장치의 전원을 다시 켜고 초기화한 후에도 데이터를 수신하지 못했기 때문에 응답 데이터를 제공하지 않아 recvfrom 함수가 작동하지 않게 됩니다. 움직이지 마세요.

이러한 코드가 사이트에 공개되면 위의 상황이 현장에서 발생하는 것은 정상이므로 숨겨진 위험이 커집니다. 예를 들어 CPU 장치의 전원이 먼저 켜지고 나중에 외부 장치의 전원이 켜지는 경우 위와 같은 문제도 발생하게 됩니다. 이전 프로젝트에서는 이 문제로 인해 고객이 제품 문제에 대해 불만을 표시했습니다. 고객은 통신이 실패하면 장치의 전원을 다시 꺼야 문제가 해결될 수 있다는 사실을 알게 되었습니다.

解决上述问题的办法也很简单,可以设置一个超时,使用setsockopt函数,让接收函数在超时时间内没有接收到数据时就返回就行了。返回后再接着重头发送数据即可,框架如下:

/* 设置阻塞超时 */

struct timeval timeout = {3, 0}; // 设置3s超时

if(setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval))

{

<code style="display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px">printf("time out setting failed ");
</code>

}

.

.

.

/* 数据阻塞接收 */

int receivePacketLen = recvfrom(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)& addr_sender,&addrLen);

if(receivePacketLen != -1)

{

//接收到数据

}

else if (errno == EAGAIN) //阻塞接收超时

{

<code style="display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px">printf("udp receive timeout! ");

return -1;
</code>

}

为了大家更直观的感受这个问题,我们在上面实验的基础上来模拟这个场景,我们先运行upd_client,后运行udp_server,大家看下现象,结果自然是没有数据输出。

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

道理不难想明白,client程序运行后,先发送了数据,然后就阻塞在读那里不动了。我们把程序简单修改下:

// Max Recv block timeout in second
#define gMaxRecvBlockTimeout 3
…
…
…
// Set recv timeout
   struct timeval timeout = {gMaxRecvBlockTimeout, 0};
   if(setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval)) printf("time out setting failed ");
   }


//不断获取用户输入并发送给服务器,然后接受服务器数据
while(1)
{
char buff[1024] = "Hello TopSemic Friends!";
//printf("Please Input a string: ");
//fgets(buff,1024,stdin);


sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr*)&addr_dest, sizeof(struct sockaddr_in));
recvlen = recvfrom(sockfd,buff,sizeof(buff),0,(struct sockaddr *)&addr_sender,(socklen_t *)&nlen);
   if(recvlen > 0)
{
buff[recvlen] = 0x00;
printf("Message form server: %s ", buff);
printf("sender ip:%s port:%d ",inet_ntoa(addr_sender.sin_addr),ntohs(addr_sender.sin_port));
}
else if(errno == EAGAIN) // 阻塞接收超时
       {
           printf("udp receive timeout! ");
       }
printf("************************************** ");
}


close(sockfd);
    return 0;

这时我们先运行client,

임베디드 Linux 시리즈 8부: 네트워크 포트 작동

打印如上,然后再运行Server,就可以正常工作了,不会再出现上述问题。

위 내용은 임베디드 Linux 시리즈 8부: 네트워크 포트 작동의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 lxlinux.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제