您的位置:首页 > 其它

让我们的ARM开发板也能“聊天”

2012-11-13 12:45 134 查看
转载申明来自: http://mcuos.com/thread-8305-1-1.html

原文作者: 郭文学 <guowenxue@gmail.com QQ:281143292>

关于凌云嵌入式: http://mcuos.com/thread-7178-1-1.html

由于项目的需要,今天给刚开始接触Linux编程的同事写了一个非常简单的socket
sample程序(代码原型来自W.
Richard Stevens的《Unix Network Program 3rd》,在这里向Richard Stevens致敬!),但这个“小程序”里暗藏着“大道理”:他能够让我们的开发板“聊天”,不相信?那你就认真分析一下:

在我们服务器上跑的服务器程序:

[guowenxue@centos6 unp]$ cat socket_server.c

/*********************************************************************************

* Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com> * All rights reserved.

* Thanks To: W. Richard Stevens, who bring me to the Linux program world!*

* Filename: socket_server.c

* Description: This is a sample socket server demo program.

*

* Version: 1.0.0(06/08/2012~)

* Author: Guo Wenxue <guowenxue@gmail.com>

* ChangeLog: 1, Release initial version on "06/08/2012 02:50:51 PM"

*

********************************************************************************/

/* Some Unix Program Standard head file */

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

/* Socket Program head file */

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h> /* sockaddr_in{} and other Internet define */

/* strerror(), perror(), errno head file*/

#include <errno.h>

#include <time.h>

#define DAYTIME_SERV_PORT 8888

#define MAX_BUF_SIZE 1024

#define LISTENQ 1024 /* 2nd argument to listen () */

/* Argc is the program linux running command arguments count, and argv is

* the arguments string value. All of the arguments take as string.

* For example:

* If we run this program as "./socket_server 13", there are two

* arguments, so argc=2, argv[0] is the first argument, which is string value

* "./socket_server" and argv[1] is the second argment, which is string value

* "13".

*/

int main(int argc, char **argv)

{

int listenfd, connfd;

char send_buf[MAX_BUF_SIZE];

struct sockaddr_in servaddr;

time_t ticks;

int serv_port;

if(2 == argc)

{

/*If there is a argument, then we take it as the server port number, and it's a

*string value, so we use atoi() function to convert it to int type.*/

serv_port = atoi(argv[1]);

}

else

{

serv_port = DAYTIME_SERV_PORT;

printf("Another Usage: %s [ServerPort]\n", argv[0]);

}

/*

* Open an IPV4(AF_INET) TCP(SOCK_STREAM) Socket File Description(listenfd), UDP socket

* should use SOCK_DGRAM,We can use linux command "man socket" to see this function manual

*/

if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

{

/* strerror() is the 1st way to display the failure reason, argument

* errno is a globle variable defined in <errno.h>, we can use linux command

* "man strerror" to see this function manual*/

printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno));

return -1;

}

/* Now we set the Server Information, include IPV4 or IPV6, Listen IP address and Port */

memset(&servaddr, 0, sizeof(servaddr));

servaddr.sin_family = AF_INET; /* Set it as IPV4 protocal */

servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Listen all the local IP address */

servaddr.sin_port = htons(serv_port); /* daytime server port */

/*

* When a socket is created with socket(2), it exists in a name space (address family) but

* has no address assigned to it. bind() assigns the address specified to by addr to the

* socket referred to by the file descriptor listenfd. We can use Linux command "man 2 bind"

* to see this function manual.

*/

if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)

{

printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));

goto CleanUp;

}

/*

* listen() marks the socket referred to by listenfd as a passive socket, that is, as a socket

* that will be used to accept incoming connection requests using accept(2). We can use Linux

* command "man listen" to see this function manual.

*/

if(listen(listenfd, LISTENQ) < 0)

{

printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));

goto CleanUp;

}

printf("%s server start to listen port %d\n", argv[0],serv_port);

for ( ; ; )

{

/*

* The accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET).

* It extracts the first connection request on the queue of pending connections for the listening

* socket linstenfd, creates a new connected socket(connfd), and returns a new file descriptor referring

* to that socket. The newly created socket is not in the listening state. The original socket

* listenfd is unaffected by this call.

*/

if( (connfd=accept(listenfd, (struct sockaddr *)NULL, NULL)) > 0)

{

ticks = time(NULL); /* Get current system time */

snprintf(send_buf, sizeof(send_buf), "%.24s\r\n", ctime(&ticks));

if(write(connfd, send_buf, strlen(send_buf)) < 0)

{

printf("Write current time to client failure: %s\n", strerror(errno));

}

close(connfd);

}

}

CleanUp:

close(listenfd); /* We must close socket File Description when program exit*/

return 0;

}

复制代码

在我们的ARM开发板上跑的客户端程序:

/*********************************************************************************

* Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com>

* All rights reserved.* Thanks To: W. Richard Stevens, who bring me to the Linux program world!

*

* Filename: socket_client.c

* Description: This is a sample socket client demo program.

*

* Version: 1.0.0(06/08/2012~)

* Author: Guo Wenxue <guowenxue@gmail.com>

* ChangeLog: 1, Release initial version on "06/08/2012 02:50:51 PM"

*

********************************************************************************/

/* Some Unix Program Standard head file */

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

/* Socket Program head file */

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h> /* sockaddr_in{} and other Internet define */

#include <errno.h> /* strerror(), perror(), errno head file*/

#include <arpa/inet.h> /* For inet_pton() */

#define DAYTIME_SERV_PORT 8888

#define MAX_BUF_SIZE 1024

/* Argc is the program linux running command arguments count, and argv is

* the arguments string value. All of the arguments take as string.

* For example:

* If we run this program as "./socket_client 192.168.3.15", there are two

* arguments, so argc=2, argv[0] is the first argument, which is string value

* "./socket_client" and argv[1] is the second argment, which is string value

* "1921.68.3.15".

*/

int main(int argc, char **argv)

{

int sockfd, len, retval;

char recv_buf[MAX_BUF_SIZE];

struct sockaddr_in servaddr;

/* Check the arguments count, if No serverip address give as an argument,

* then program exit */

if(2 != argc)

{

printf("usage: %s <ServerIPaddress>\n", argv[0]);

printf("Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com>\n");

return -1;

}

/* Open an IPV4(AF_INET) TCP(SOCK_STREAM) Socket File Description, UDP socket should

* use SOCK_DGRAM,We can use linux command "man socket" to see this function manual

*/

if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

{

/* strerror() is the 1st way to display the failure reason, argument

* errno is a globle variable defined in <errno.h>, we can use linux command

* "man strerror" to see this function manual*/

printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno));

return -1;

}

/* Now we set the Server Information, include IPV4 or IPV6, Server Port, Server IP address */

memset(&servaddr, 0, sizeof(servaddr));

servaddr.sin_family = AF_INET; /* Set it as IPV4 protocal */

servaddr.sin_port = htons(DAYTIME_SERV_PORT); /* Daytime server port*/

/* argv[1] we take as Server IP address, it's the second arguments in running command */

if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)

{

printf("Use inet_pton() to set the Server IP address failure.\n");

retval = -2;

goto CleanUp;

}

/* Now call connect() function to connect to the server, we can use linux command "man connect"

* to see this function manual */

printf("Connect to Server [%s:%d]\n", argv[1], DAYTIME_SERV_PORT);

if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)

{

printf("Connect to the server [%s:%d] failure\n", argv[1], DAYTIME_SERV_PORT);

retval = -3;

goto CleanUp;

}

/* Once we connect to the server successfully, we can receive the data from the socket socekt */

while ( (len = read(sockfd, recv_buf, MAX_BUF_SIZE)) > 0)

{

recv_buf[len] = 0; /* null terminate */

printf("Get Time from server %s: %s\n", argv[1], recv_buf);

}

if( len < 0 )

{

/* perror() ss the 2nd way to display the failure reason */

perror("Read() from socket failure");

retval = -4;

goto CleanUp;

}

CleanUp:

close(sockfd); /* We must close socket File Description when program exit*/

return 0;

}

复制代码

源代码有了,我们开始编译,这里给了file和strip两个很有用的命令的用法:

让server端的程序在PC Linux上跑

[guowenxue@centos6 unp]$ gcc -static
socket_server.c -o socket_server

[guowenxue@centos6 unp]$ file socket_server

socket_server: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.18, not stripped

[guowenxue@centos6 unp]$ du -h socket_server

760K socket_server

[guowenxue@centos6 unp]$

[guowenxue@centos6 unp]$ file socket_server

socket_server: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.18, stripped

[guowenxue@centos6 unp]$ strip socket_server

[guowenxue@centos6 unp]$ du -h socket_server

688K socket_server

其实嵌入式应用程序开发,就是写完代码后的一个交叉编译的过程。那什么是交叉编译呢?简单的来说,就是用交叉编译器编译出来的程序,在另外一个处理器(如我们的ARM)上跑,而不是在本地处理器(如我们的X86)上跑。那怎么知道我们编译的程序是在X86上跑,还是在ARM平台上跑呢?那就好好研究一下file命令的输出。下面我们开始交叉编译client端程序:

[guowenxue@centos6 unp]$ /opt/buildroot-2011.11/arm926t/usr/bin/arm-linux-gcc socket_client.c -o socket_client.c

喜欢使用TAB键补齐命令的童鞋们,估计会经常碰到我上面这条悲摧的命令。如果你没碰到,够勇敢的话,可以试试。上面的命令会把编译生成的可执行文件覆盖我们的C源码文件socket_client.c.
我们使用file命令看看他的信息:

[guowenxue@centos6 unp]$ file socket_client.c

socket_client.c: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

好在哥们使用git管着我的源代码,别跟我说你不知道git/svn是啥?21世纪不知道git/svn的程序不是好程序员,如果你还真不知道,那么西元200年孙策就告诉我们,“外事不决找google,内事不决问baidu”。

[guowenxue@centos6 unp]$ git checkout socket_client.c

[guowenxue@centos6 unp]$ file socket_client.c

socket_client.c: ASCII C program text

噢,在git的帮助下,我胡汉三又回来啦!言归正传,上面通过一个失误讲解了git的一个小作用。下面来正经的:

[guowenxue@centos6 unp]$ /opt/buildroot-2011.11/arm926t/usr/bin/arm-linux-gcc -static socket_client.c -o socket_client

[guowenxue@centos6 unp]$ file socket_client

socket_client: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, not stripped

[guowenxue@centos6 unp]$ du -h socket_client

124K socket_client

通过file命令,我们可以看到,socket_client是32位小端字节序(LSB)上的ELF格式的可执行程序,在ARM上跑,是静态编译的(statically linked),没有strip. 如果没有strip,文件的大小为124K.

下面我们使用strip命令给他剥掉华丽的外衣后再看看文件类型和大小:

[guowenxue@centos6 unp]$ /opt/buildroot-2011.11/arm926t/usr/bin/arm-linux-strip socket_client

[guowenxue@centos6 unp]$ file socket_client

socket_client: ELF 32-bit LSB executable, ARM, version 1 (SYSV), statically linked, stripped

[guowenxue@centos6 unp]$ du -h socket_client

76K socket_client

是ARM的就该放到ARM上跑,是X86的,我们就应该在X86上运行。是火星上的,我们就把它扔到火星上去:

PC端上,首先把ARM上跑的socket_client放到我们的tftp服务器根目录下去:

[guowenxue@centos6 unp]$ cp socket_client /tftp/

别说,你不知道怎么在Linux上假设tftp服务器,那好好看看我的这个文章吧:

http://mcuos.com/thread-7115-1-1.html

然后在本地Linux服务器上开始运行我们的服务器端程序:

[guowenxue@centos6 unp]$ ./socket_server

Another Usage: ./socket_server [ServerPort]

./socket_server server start to listen port 8888

完成PC端的工作,我们就要跑到ARM这颗火星上来处理client端了:

ARM上跑的是Linux,用的当时最新的3.3,不过现在已经out啦!

~ >: uname -a

Linux CoherentPlus 3.3.0 #1 Thu May 3 16:45:19 CST 2012 armv5tejl GNU/Linux

首先使用tftp命令下载我们的刚才编译出来的程序,192.168.1.78是我的Linux服务器IP地址,他开启了tftp服务,tftp服务的根目录是/tftp.

~ >: tftp -gr socket_client 192.168.1.78

socket_client 100% |***********************************************************************************| 77112 0:00:00 ETA

既然是Linux,运行之前,肯定要给他执行的权限咯。

~ >: chmod 777 socket_client

火星人开始跟地球人聊天了:“地球上的兄弟,把你们当前的时间告诉我吧!”

地球上的兄弟很爽快的答应:“哥们,我们现在的时间是Fri Jun 8 17:47:04 2012,欢迎光临!”

~ >: ./socket_client

usage: ./socket_client <ServerIPaddress>

Copyright: (C) 2012 Guo Wenxue<guowenxue@gmail.com>

~ >: ./socket_client 192.168.1.78

Connect to Server [192.168.1.78:8888]

Get Time from server 192.168.1.78: Fri Jun 8 17:47:04 2012

~ >:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: