1、TCP
sockaddr_in
sockaddr_in结构体
sockaddr_in(在netinet/in.h中定义):
structsockaddr_in {
short sin_family;
/*Addressfamily一般来说AF_INET(地址族)PF_INET(协议族)*/
unsigned short sin_port;
/*Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字*/
structin_addr sin_addr;/*Internetaddress*/
unsigned char sin_zero[8];
/*Samesizeasstructsockaddr没有实际意义,只是为了 跟SOCKADDR结构在内存中对齐*/
};
int socket(int domain, int type, int protocol);
socket()函数用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源
socket(AF_INET, SOCK_STREAM, 0)
domain目前仅支持AF_INET格式,也就是说ARPA Internet地址格式
typetype新套接口的类型描述,SOCK_STREAM 提供面向连接的稳定数据传输,即TCP协议。
protocol套接口所用的协议。如调用者不想指定,可用0指定,表示缺省。
bzero inet_addr
extern void bzero(void *s, int n);
bzero(&s_add,sizeof(struct sockaddr_in))
s 要置零的数据的起始地址;
n 要置零的数据字节个数。
in_addr_t inet_addr(const char *cp)
inet_addr(argv[1])
inet_addr()的功能是将一个点分十进制的IP转换成一个长整数型数(u_long类型)
u_short htons( u_short hostshort)
将主机的无符号短整形数转换成网络字节顺序
s_add.sin_port=htons(portnum)
hostshort:主机字节顺序表达的16位数
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen)
建立与一个端口的连接
sockfd标识一个未连接套接口的描述字
serv_addr套接字s想要连接的主机地址和端口号
addrlen缓冲区name的长度
bind listen
int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen)
bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号)
int listen(SOCKET sockfd, int backlog)
listen函数使用主动连接套接字变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。
if(-1 == listen(sfp,5))
sockfd 一个已绑定未被连接的套接字描述符
backlog 连接请求队列的最大长度
accept ntohl ntohs send
int accept(int sockfd, struct sockaddr* addr, socklen_t* len)
accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字
uint32_t ntohl(uint32_t netlong);
ntohl()是将一个无符号长整形数从网络字节顺序转换为主机字节顺序
netlong:一个以网络字节顺序表达的32位数
uint16_t ntohs(uint16_t netshort)
ntohs将一个无符号短整形数从网络字节顺序转换为主机字节顺序
netshort:一个以网络字节顺序表达的16位数
ssize_t send (int s,const void *msg,size_t len,int flags)
send(nfp, buffer, strlen(buffer), 0)
s指定发送端套接字描述符
msg一个存放应用程式要发送数据的缓冲区
len参数指明实际要发送的数据的字节数
flags参数一般置0
server.c
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(void)
{
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");
// socket
sfp = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sfp){
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
// sockaddr_in config
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);
// bind
if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr))){
printf("bind fail !\r\n");
return -1;
}
printf("bind ok !\r\n");
// listen
if(-1 == listen(sfp,5)){
printf("listen fail !\r\n");
return -1;
}
printf("listen ok\r\n");
// accept
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);
}
// close fp
close(nfp);
close(sfp);
return 0;
}
client.c
#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;
}
// socket
cfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == cfd){
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
// sinaddr_in config
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);
// connect
if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr))){
printf("connect fail !\r\n");
return -1;
}
printf("connect ok !\r\n");
// read recv
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
close(cfd);
return 0;
}
2、UDP
sendto
ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len)
sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, sizeof(servaddr))
socket一个标识套接口的描述字
message包含待发送数据的缓冲区
length buf缓冲区中数据的长度
flags调用方式标志位
dest_addr指针,指向目的套接口的地址
dest_lento所指地址的长度
recvfrome
ssize_t recvfrom(int sockfd,void *buf,int len,unsigned int flags, struct sockaddr *from,socket_t *fromlen);
recvfrom(sockfd, recvline, 1024, 0, NULL, NULL)
sockfd:标识一个已连接套接口的描述字
buf接收数据缓冲区
len缓冲区长度
flags调用操作方式。是以下一个或者多个标志的组合体,可通过or操作连在一起
from(可选)指针,指向装有源地址的缓冲区
fromlen(可选)指针,指向from缓冲区长度值
UDPserver.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char **argv)
{
int n;
char recvline[1024] = {0};
int sockfd;
struct sockaddr_in servaddr;
/* 创建一个UDP连接的socket */
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
/* 变量servaddr清零 */
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(50001);
/* 绑定servaddr到创建的socket上 */
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
/* 接收客户端发送的数据 */
recvfrom(sockfd, recvline, 1024, 0, NULL, NULL);
printf("%s\n", recvline);
/* 关闭socket连接 */
close(sockfd);
}
UDPclient.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if(argc != 2){
printf("usgae: ./client [ip]\n");
return -1;
}
/* 创建一个UDP的socket连接 */
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
/* 变量servaddr清零 */
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(50001);
servaddr.sin_addr.s_addr = inet_addr(argv[1]);
char sendline[100];
sprintf(sendline, "Hello, world!");
/* 发送数据 */
sendto(sockfd, sendline, strlen(sendline), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
/* 关闭socket连接 */
close(sockfd);
return 1;
}
3、web 服务器的搭建(boa)
小型的web服务器boa
boa的官方网站为www.boa.org
第一步:boa的拷贝解压
官网地址www.boa.org
使用ssh软件拷贝到Ubuntu系统中(arm的Ubuntu环境)
解压#tar -vxf boa-0.94.13.tar.gz
#cd boa-0.94.13
第二步:boa生成Makefile编译文件
#cd src
运行#./configure
当前目录下生成Makefile编译文件
第三步:修改Makefile里面的两个参数
#vi Makefile(或者#vim Makefile)
查找到有CC = gcc的行,在31行左右(使用vi编辑器的查找命令#/CC = gcc)
替换为CC = arm-none-linux-gnueabi-gcc -static 修改编译器
查找到有CPP = gcc -E的行,在32行左右
CPP = arm-none-linux-gnueabi-gcc -E -static
保存退出
输入#make 开始编译
第四步:编译过程中会出现错误
#vi compat.h
查找到有#define TIMEZONE_OFFSET(foo) foo##->tm_gmtoff的行,在123行左右,替换为#define TIMEZONE_OFFSET(foo) foo->tm_gmtoff
保存退出
继续编译
第五步:生成boa文件,给boa瘦身
#ls查看boa是否生成
#ll boa查看其大小932743
使用瘦身命令#arm-none-linux-gnueabi-strip boa
#ll boa查看其大小642340
第六步:NFS系统文件处理
拷贝编译生成的boa到NFS文件系统的bin目录下
在NFS文件系统的etc目录下建立boa文件夹
在NFS文件系统的目录下建立www文件夹
在NFS文件系统的www目录下面建立文件夹cgi-bin目录
第七步:添加配置文件和用户组
拷贝boa-0.94.13目录下面默认的boa.conf到NFS文件系统的etc/boa中
拷贝虚拟机下面/etc目录下的mime.types到NFS文件系统的etc目录下面
第八步:用户组,如果使用的是QT裁剪过来的则不需要执行这一步
在NFS文件系统的etc目录下用命令#vi group命令建立group文件
在group文件中添加root:*:0:
第九步:修改配置文件boa.conf
进到NFS文件系统的etc/boa目录,打开boa.conf文件#vi boa.conf
查找到有Group nogroup的行,在50行左右,替换为Group root
查找到有#ServerName www.your.org.here的行,在95行左右,替换为ServerName www.your.org.here
查找到有DocumentRoot /var/www的行,在115行左右,替换为DocumentRoot /www(注意:这里的“/www”就是前面步骤我们使用mkdir创建的www目录)
查找到有ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/的行,在194行左右,替换为ScriptAlias /cgi-bin/ /www/cgi-bin/
第十步:添加自动运行脚本,建立网页
打开etc/init.d/rcS文件,定位到最后一行
添加boa &到自动启动,保存退出
进入到前面创建的www目录
vi index.html命令建立index.html
拷贝网页代码,保存退出
测试
启动开发板,console中输入#ps命令可以看到boa在运行
然后使用任意内网设备,登录http://192.168.1.14 默认地址,如果修改过则需要使用修改过的地址
index_web.c
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>iTOP-4412</title>
<style type="text/css">
<!--
.STYLE1 {font-size: 36px}
body {
background-color: #996600;
}
-->
</style>
</head>
<body>
<table width="613" height="424" border="0" align="center">
<tr>
<td colspan="2" align="center" valign="middle"><h2 class="STYLE1">iTOP-4412 WEB SERVER </h2></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
</table>
</body>
</html>
4、web 页面远程控制led
CGI(Common Gateway Interface)是外部应用扩展,应用程序与www服务器交互的一个标准接口。按照CGI标准编写的外部扩展应用程序可以处理客户端浏览器输入的数据,从而完成客户端与服务器的交互操作。而CGI规范就定义了web服务器如何向扩展应用程序发送消息,在收到扩展应用程序的信息后又如何进行处理等内容。通过CGI可以提供许多静态的HTM L网页无法实现的功能。比如搜索引擎、基于web的数据库访问等等。
第一步:替换index.html代码
进入上期视频建立的www目录中,打开index.html
拷贝新的程序代码到index.html中
上面输入的是HTML格式的代码,主要是用到了通过表单向服务器提交信息,在表单里面指定了服务器端处理接收到信息的CGI程序是myled,这是在form表单的属性里设置的,代码是“form action="/cgi-bin/myled.cgi" method="get”,使用的传递数据的方式是get方法
第二步:CGI程序
打开在etc/boa中的boa.conf文件
查找ScriptAlias /cgi-bin/ /www/cgi-bin/,这是指定的CGI程序的存储目录
第三步:myled.c
进入www/cgi-bin目录
将myled.c拷贝进去
编译#arm-none-linux-gnueabi-gcc -o myled.cgi myled.c -static
修改权限#chmod 777 myled.cgi
myled.c
printf函数输出的基本格式,简单明了,容易懂
char *getenv(char *envvar)
getenv("QUERY_STRING")
该函数用来取得参数envvar环境变量的内容
QUERY_STRING获取网页传过来的数据
启动开发板,运行局域网中的浏览器
浏览器中输入192.168.1.231(根据实际设置为准)
简单的操作界面控制led灯的亮灭
index_led.c
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>led远程控制</title>
<style type="text/css">
body {
background-color: #999900;
text-align: center;
}
.ziti {
font-size: 24px;
}
.juzhong {
text-align: center;
}
.hsz {
text-align: center;
}
.hsz td {
color: #00F;
font-size: 18px;
}
.hsz {
background-color: #FCC;
}
.juzhong table {
text-align: center;
}
.juzhong table tr {
}
#h1 {
background-color: #0FC;
}
#h2 {
background-color: #FF9;
}
.h3 {
background-color: #0CF;
}
.ys1 {
font-size: 24px;
}
.STYLE1 {font-size: 36px}
</style>
</head>
<body class="juzhong">
<table width="900" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td><p class="STYLE1"> </p>
<p class="STYLE1">iTOP-4412 WEB SERVER </p></td>
</tr>
<tr>
<td height="30"> </td>
</tr>
<tr>
<td><form action="/cgi-bin/myled.cgi" method="get" enctype="application/x-www-form-urlencoded" name="form1" target="_blank" id="form1">
<table width="300" border="1" align="center" cellpadding="1" cellspacing="1">
<tr>
<td>Led1</td>
<td><input name="led1" type="checkbox" id="led1" value="1" />
<label for="led1"></label></td>
</tr>
<tr>
<td>Led2</td>
<td><input name="led2" type="checkbox" id="led2" value="2" />
<label for="led2"></label></td>
</tr>
<tr>
<td colspan="2"><input type="submit" name="submit" id="submit" value="submit" /></td>
</tr>
</table>
</form></td>
</tr>
<tr>
<td ><p> </p> </td>
</tr>
</table>
<p> </p>
</body>
</html>
myled.c
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *data;
int leds[2] = {0, 0};
long m, n;
int exit=0,i,fd;
printf("Content-Type:text/html;charset=gb2312\n\n");
printf("<html>\n");
printf("<body>\n");
printf("<title>iTOP-4412</title> ");
printf("<h3>iTOP-4412</h3> ");
data = getenv("QUERY_STRING");
printf("<p>receive data:%s</p>",data);
while(*data != '\0')
{
if(*data=='=')
switch(*(data+1))
{
case '1':leds[0]=1;break;
case '2':leds[1]=1;break;
default:exit=1;break;
}
if(exit == 1)
break;
data++;
}
fd=open("/dev/leds",0);
for(i=0;i<2;i++)
{
if(leds[i]==1)
printf("<p>%d\t</p>",i+1);
ioctl(fd,leds[i],i);
}
printf("</body>\n");
printf("</html>\n");
return 0;
}