基于HHARM9-EDU的TCP/IP(UDP)协议的实现
嵌入式技术的发展日新月异,现如今,嵌入式设备已经广泛应用于各种网络,本文简要地说明一下如何实现PC与HHARM9-EDU之间的TCP/IP(UDP) 通讯。
① 回顾一下什么是TCP/IP协议:
传统的开放式系统互连参考模型,是一种通信协议的7层抽象的参考模型,其中每一层执行某一特定任务。该模型的目的是使各种硬件在相同的层次上相互通信。这7层是:物理层、数据链路层、网路层、传输层、话路层、表示层和应用层。
TCP/IP起源于美国国防部高级研究规划署(DARPA)的一项研究计划——实现若干台主机的相互通信。现在TCP/IP已成为Internet上通信的工业标准。
TCP/IP通讯协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。这4层分别为:
应用层:应用程序间沟通的层,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等。
传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据,并且确定数据已被送达并接收。
网络层:负责提供基本的数据封包传送功能,让每一块数据包都能够到达目的主机(但不检查是否被正确接收),如网际协议(IP)。
网络接口:对实际的网络媒体的管理,定义如何使用实际网络(如Ethernet、Serial Line等)来传送数据。
TCP/IP与OSI参考模型的对应关系如下:
② 网络传输中另一个重要概念――端口:
按照
OSI七层模型的描述,传输层提供进程(应用程序)通信的能力。为了标识通信实体中进行通信的进程(应用程序),TCP/IP协议提出了协议端口(protocol port,简称端口)的概念。
端口是一种抽象的软件结构(包括一些数据结构和
I/O缓冲区)。应用程序通过系统调用与某端口建立连接(binding)后,传输层传给该端口的数据都被相应的进程所接收,相应进程发给传输层的数据都通过该端口输出。
端口用一个整数型标识符来表示,即端口号。端口号跟协议相关,
TCP/IP传输层的两个协议TCP和UDP是完全独立的两个软件模块,因此各自的端口号也相互独立。
端口使用一个
16位的数字来表示,它的范围是0~65535,1024以下的端口号保留给预定义的服务。例如:http使用80端口。
③ 套接字(socket)的引入:
为了能够方便的开发网络应用软件,由美国伯克利大学在
Unix上推出了一种应用程序访问通信协议的操作系统调用socket(套接字)。socket的出现,使程序员可以很方便地访问TCP/IP,从而开发各种网络应用的程序。
随着
Unix的应用推广,套接字在编写网络软件中得到了极大的普及。后来,套接字又被引进了Windows等操作系统,成为开发网络应用程序的非常有效快捷的工具。
套接字存在于通信区域中。通信区域也叫地址族,它是一个抽象的概念,主要用于将通过套接字通信的进程的共有特性综合在一起。套接字通常只与同一区域的套接字交换数据(也有可能跨区域通信,但这只在执行了某种转换进程后才能实现)。
Windows Sockets只支持一个通信区域:网际域( AF_INET),这个域被使用网际协议簇通信的进程使用。
④ 什么是UDP协议:
UDP是一个无连接协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,
UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。
基于
UDP(面向无连接)的socket编程:
⑴服务器端(接收端)程序:
1、创建套接字(
socket)。
2、将套接字绑定到一个本地地址和端口上(
bind)。
3、等待接收数据(
recvfrom)。
4、关闭套接字。
⑵客户端(发送端)程序:
1、创建套接字(
socket)。
2、向服务器发送数据(
sendto)。
3、关闭套接字。
⑤ 具体编程实现如下:
一共有两个程序:服务器端(接收端)程序和客户器端(发送端)程序
服务器端程序运行在实验箱上,客户端程序运行在
PC上,从客户端发送消息,服务端收到消息显示在触摸屏上,并回复一条消息给客户端。
下面分别说明:
⑴服务器端(接收端)程序:
服务器端程序运行在实验箱上。
首先在
PC上调试,操作系统是RedHat 9,调试工具为MINIGUI V1.3.3,MINIGUI上调试完成后,交叉编译后在实验箱上成功运行,效果图如下:
附:源代码
#include <minigui/dti.c>
#include <stdio.h>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <minigui/common.h>
#include <minigui/minigui.h>
#include <minigui/gdi.h>
#include <minigui/window.h>
#include <minigui/control.h>
#include <minigui/mywindows.h>
#include <minigui/mgext.h>
#define IDC_static
100
#define IDC_listbox
200
#define IDC_edit
300
#define IDC_send
400
#define PORT 1234
/* 端口号
*/
#define MAXDATASIZE 100
/* 最大字节数
*/
char tempBuf[200];
int sockfd; /* socket 描述符
*/
struct sockaddr_in server; /* 服务端地址信息
*/
struct sockaddr_in client; /* 客户端地址信息
*/
int sin_size;
int num;
char msg[MAXDATASIZE];
initserver(HWND hDlg)
{
/* 建立
UDP socket */
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("Creating socket failed.");
exit(1);
}
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr.s_addr = htonl (INADDR_ANY);
if (bind(sockfd, (struct sockaddr *)&server,
sizeof(struct sockaddr)) == -1)
{
perror("Bind error.");
exit(1);
}
}
CTRLDATA CtrlTest[]=
{
{
"static",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON| WS_EX_TRANSPARENT,
0, 50, 240, 200,
IDC_static,
"message",
0
},
{
"listbox",
WS_CHILD | WS_VISIBLE | WS_VSCROLL,
0, 0, 240, 45,
IDC_listbox,
"ip",
0
},
{
"edit",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON| WS_EX_TRANSPARENT,
5, 255, 160, 25,
IDC_edit,
"",
0
},
{
"button",
WS_CHILD | WS_VISIBLE,
175, 255, 50, 25,
IDC_send,
"send",
0
},
};
DLGTEMPLATE DlgTest =
{
WS_VISIBLE | WS_CAPTION | WS_BORDER| WS_EX_TRANSPARENT,
WS_EX_NONE,
0, 0, 240, 320,
"UDP Test",
0, 0,
4, CtrlTest,
0
};
/************************************************************************/
static int TestWinProc(HWND hDlg, int message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case MSG_INITDIALOG:
initserver(hDlg);
SetTimer(hDlg,1,10);
break;
case MSG_TIMER:
sin_size=sizeof(struct sockaddr_in);
num = recvfrom(sockfd,msg,MAXDATASIZE,0,
(struct sockaddr *)&client,&sin_size);
if (num < 0){
perror("recvfrom error/n");
exit(1);
}
msg[num] = '/0';
printf("You got a message (%s) from %s/n",
msg,inet_ntoa(client.sin_addr) );
SetWindowText (GetDlgItem (hDlg, IDC_static), msg);
sprintf(tempBuf,"Welcome %s to my server.",
inet_ntoa(client.sin_addr));
sendto(sockfd,tempBuf,strlen(tempBuf)+1,0,
(struct sockaddr *)&client,sin_size);
if (!strcmp(msg,"quit")) break;
break;
case MSG_COMMAND:
switch(wParam) {
case IDC_send:
break;
}
break;
case MSG_CLOSE:
{
EndDialog (hDlg, 0);
}
return 0;
}
return DefaultDialogProc (hDlg, message, wParam, lParam);
}
/************************************************************************/
int MiniGUIMain (int argc, const char* argv[])
{
DlgTest.controls = CtrlTest;
DialogBoxIndirectParam (&DlgTest, HWND_DESKTOP,
TestWinProc, 0L);
return 0;
}
⑵客户器端(发送端)程序:
客户端程序可以在选择在
Linux下运行,或者在Windows下运行,下面分别说明如何具体实现:
㈠Linux下:
我的
Linux系统版本为RedHat 9,在Linux下的开发程序,由于没有像VC之类的强大开发工具,所以编程比较麻烦,调试起来也不方便,Linux下的程序采用存C编写,本程序没有编写图形界面,只开发了一个类似Windows控制台的程序,在功能上实现了PC与实验箱之间的通信,效果图如下:
附:源代码
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 1234 /*断口号
*/
#define MAXDATASIZE 100
int main(int argc, char *argv[])
{
int fd, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in server,reply; /* 服务器地址信息
*/
if (argc !=3)
{
printf("Usage: %s <IP Address> <message>/n",argv[0]);
exit(1);
}
if ((he=gethostbyname(argv[1]))==NULL)
{
printf("gethostbyname() error/n");
exit(1);
}
if ((fd=socket(AF_INET, SOCK_DGRAM, 0))==-1)
{
printf("socket() error/n");
exit(1);
}
bzero(&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr = *((struct in_addr *)he->h_addr);
sendto(fd, argv[2], strlen(argv[2]),0,
(struct sockaddr *)&server,sizeof(struct sockaddr));
while (1) {
int len;
recvfrom(fd,buf,MAXDATASIZE,0,(struct sockaddr *)&reply,&len);
printf("Server Message: %s/n",buf);
break;
}
close(fd);
}
㈡Windows下:
我的
Windows版本为Windows XP,在Windows下编写程序就方便多了,由于有了VC,一切工作就简单了一下,在这里我写了两个版本的程序:
1、 Windows控制台程序
2、 图形界面程序
下面分别说明:
1、
Windows
控制台程序
本程序采用
VC6编写,Windows下编写网络程序与在Linux下编写不太一样,主要是socket的调用有较大的不同,此外VC6主要采用C++语言,效果图如下:
附:源代码
#include <Winsock2.h>
#include <stdio.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("192.168.2.120");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(1234);
char recvBuf[100];
char sendBuf[100];
char tempBuf[200];
int len=sizeof(SOCKADDR);
while(1)
{
printf("Please input data:/n");
gets(sendBuf);
sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,
(SOCKADDR*)&addrSrv,len);
recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrSrv,&len);
if('q'==recvBuf[0])
{
sendto(sockClient,"q",strlen("q")+1,0,
(SOCKADDR*)&addrSrv,len);
printf("Chat end!/n");
break;
}
sprintf(tempBuf,"%s say : %s",inet_ntoa(addrSrv.sin_addr),recvBuf);
printf("%s/n",tempBuf);
}
closesocket(sockClient);
WSACleanup();
}
2、
图形界面程序
本程序采用
VC6编写,VC6提供了强大的图形界面程序编写工具,让我们开发图形界面程序十分方便,效果图如下:
图形界面程序源代码太长,在这里就不写上了。