一、作业要求
1、利用TCP Socket实现PC机与目标板的通讯,目标板作为服务端,服务端按要求给多个个客户端同时传送不同的文件(可以使用多线程机制、非阻塞或异步式处理)。
作业要求:1)交电子文档,需要源代码。
- 上机演示
2、试找一个字符型驱动程序实例进行分析。
作业要求:1)交电子文档,需要源代码。
二、作业原理与内容
1、利用TCP Socket实现PC机与目标板的通讯,目标板作为服务端,服务端按要求给多个个客户端同时传送不同的文件(可以使用多线程机制、非阻塞或异步式处理)。
作业要求:1)交电子文档,需要源代码。
- 上机演示
2、试找一个字符型驱动程序实例进行分析。
作业要求:1)交电子文档,需要源代码。
三、实验软硬件环境
虚拟机软件、一台计算机、开发板
四、作业过程(步骤、记录、数据、分析)
按步骤依次写出程序、运行结果等(可截屏)
1、利用TCP Socket实现PC机与目标板的通讯,目标板作为服务端,服务端按要求给多个个客户端同时传送不同的文件(可以使用多线程机制、非阻塞或异步式处理)。
解:服务器server端代码:
#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <pthread.h>
#define portnum 12345
#define FILE_SIZE 500
#define BUFFER_SIZE 1024
void *net_thread(void * fd);
int main()
{
//初始化套接字
int server_fd=socket(AF_INET,SOCK_STREAM,0);
if(-1==server_fd)
{
perror("socket");
exit(1);
}
//绑定端口和ip;
struct sockaddr_in server_addr; //struct sockaddr_in为结构体类型 ,server_addr为定义的结构体
server_addr.sin_family=AF_INET; //Internet地址族=AF_INET(IPv4协议)
server_addr.sin_port=htons(portnum); //将主机字节序转化为网络字节序 ,portnum是端口号
(server_addr.sin_addr).s_addr=htonl(INADDR_ANY); //IP地址
if(-1==bind(server_fd,(struct sockaddr *)&server_addr,sizeof(server_addr))) //套接字与端口绑定
{
perror("bind");
exit(6);
}
//开启监听
if(-1==listen(server_fd,5)) //5是最大连接数,指服务器最多连接5个用户
{
perror("listen");
exit(7);
}
while(1)
{
struct sockaddr_in client_addr;
int size=sizeof(client_addr);
int new_fd=accept(server_fd,(struct sockaddr *)&client_addr,&size);
//server_fd服务器的socket描述字,&client_addr指向struct sockaddr *的指针,&size指向协议地址长度指针
if(-1==new_fd)
{
perror("accept");
continue; //进行下一次循环
}
printf("accept client ip:%s:%d\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);
//inet_ntoa将一个十进制网络字节序转换为点分十进制IP格式的字符串。
printf("new_fd=%d\n",new_fd);
// 打开文件,存入客户端的文件描述符
FILE *file_fp = fopen("01.file_fp", "w+");
if(NULL == file_fp)
{
printf(" open 01.file_fp failure\n" );
exit(1);
}
else
{
int a=new_fd;
fprintf(file_fp,"%d\n",new_fd);
fclose(file_fp);
}
int pthread_id;
int ret = pthread_create((pthread_t *)&pthread_id,NULL,net_thread,(void *)&new_fd);
if(-1==ret)
{
perror("pthread_create");
close(new_fd);
continue;
}
}
close(server_fd);
return 0;
}
void *net_thread(void * fd)
{
pthread_detach(pthread_self()); //线程分离
int new_fd=*((int *)fd);
int file2_fp;
while(1)
{
// recv函数接收数据到缓冲区buffer中
char buffer[BUFFER_SIZE];
memset( buffer,0, sizeof(buffer) );
if(read(new_fd, buffer, sizeof(buffer)) < 0)
{
perror("Server Recieve Data Failed:");
break;
}
// 然后从buffer(缓冲区)拷贝到file_name中
char file_name[FILE_SIZE];
memset( file_name,0, sizeof(file_name) );
strncpy(file_name, buffer, strlen(buffer)>FILE_SIZE?FILE_SIZE:strlen(buffer));
memset( buffer,0, sizeof(buffer) );
printf("%s\n", file_name);
if( strcmp(file_name,"null")==0 )
{
break;
close(new_fd);
}
// 打开文件并读取文件数据
file2_fp = open(file_name,O_RDONLY,0777);
if(file2_fp<0)
{
printf("File:%s Not Found\n", file_name);
}
else
{
int length = 0;
memset( buffer,0, sizeof(buffer) );
// 每读取一段数据,便将其发送给客户端,循环直到文件读完为止
while( (length = read(file2_fp, buffer, sizeof(buffer))) > 0 )
{
if( write(new_fd, buffer, length) < 0)
{
printf("Send File:%s Failed.\n", file_name);
break;
}
memset( buffer,0, sizeof(buffer) );
}
// 关闭文件
close(file2_fp);
printf("File:%s Transfer Successful!\n", file_name);
}
}
close(new_fd);
}
客户端client代码:
#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#define portnum 12345
#define FILE_SIZE 500
#define BUFFER_SIZE 1024
int sendfile(int sockfd);
int main()
{
char name[30]={0};
printf("请输入服务器的主机名或者ip\n");
scanf("%s",name);
struct hostent *h;
//获取服务器信息
h=gethostbyname(name);
if(NULL==h)
{
perror("geyhostbyname");
exit(1);
}
//初始化套接字
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==sockfd)
{
perror("socket");
exit(2);
}
struct sockaddr_in server_addr;
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnum);
server_addr.sin_addr=*((struct in_addr *)h->h_addr_list[0]);
if(-1==connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)))
{
perror("connect");
exit(3);
}
while(1)
{
sendfile(sockfd);
}
return 0;
}
int sendfile(int sockfd)
{
// 输入文件名 并放到缓冲区buffer中等待发送
int file_fp;
char file_name[FILE_SIZE];
memset( file_name,0, sizeof(file_name) );
printf("Please Input File Name On Server: ");
scanf("%s", file_name);
char buffer[BUFFER_SIZE];
memset( buffer,0, sizeof(buffer) );
strncpy(buffer, file_name, strlen(file_name)>sizeof(buffer)?sizeof(buffer):strlen(file_name));
// 向服务器发送buffer中的数据
if(write(sockfd, buffer, sizeof(buffer)) < 0)
{
perror("Send File Name Failed:");
exit(1);
}
if( strcmp(file_name,"null")==0 )
{
exit(1);
close(sockfd);
}
// 打开文件,准备写入
file_fp = open(file_name,O_CREAT|O_RDWR,0777);
if( file_fp<0 )
{
printf("File:\t%s Can Not Open To Write\n", file_name);
exit(1);
}
// 从服务器接收数据到buffer中
// 每接收一段数据,便将其写入文件中,循环直到文件接收完并写完为止
int length = 0;
memset( buffer,0, sizeof(buffer) );
while((length = read(sockfd, buffer, sizeof(buffer))) > 0)
{
if( write( file_fp, buffer, length ) < length)
{
printf("File:\t%s Write Failed\n", file_name);
break;
}
if(length < sizeof(buffer))
{
break;
}
memset( buffer,0, sizeof(buffer) );
}
// 接收成功后,关闭文件,关闭socket
printf("Receive File:\t%s From Server IP Successful!\n", file_name);
close(file_fp);
}
②测试server与client的连通性
③从服务器传送xxx文件到客户端,客户端从接收到文件xxx,如下图所示:
2、试找一个字符型驱动程序实例进行分析。
用gcc编写出wwcom_writer可执行文件
/* com_writer.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include "uart_api.h"
//#include "uart_apiarm.h"
#define BUFFER_SIZE 1024
#define HOST_COM_PORT 1
int main(void)
{
int fd;
char buff[BUFFER_SIZE];
if((fd = open_port(HOST_COM_PORT)) < 0) /* 打开串口 */
{
perror("open_port");
return 1;
}
//printf("ok");
if(set_com_config(fd, 115200, 8, 'N', 1) < 0) /* 配置串口 */
{
perror("set_com_config");
return 1;
}
do
{
printf("Input some words(enter 'quit' to exit):");
memset(buff, 0, BUFFER_SIZE);
if (fgets(buff, BUFFER_SIZE, stdin) == NULL)
{
perror("fgets");
break;
}
if(write(fd, buff, strlen(buff))>0)
{
printf("The sended words are : %s", buff);
}
printf("ok\n");
} while(strncmp(buff, "quit", 4));
do
//while(1)
{
memset(buff, 0, BUFFER_SIZE);
printf("start read\n");
if (read(fd, buff, BUFFER_SIZE) > 0)
//read(fd, buff, BUFFER_SIZE);
{
printf("The received words are : %s\n", buff);
printf("success\n");
}
// else
// {
// printf("receive not success");
//}
sleep(2);
} while(strncmp(buff, "ok", 2));
close(fd);
return 0;
}
用arm-linux-gcc编译出wwcom_reader可执行文件
/* com_reader.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include "uart_apiarm.h"
//#include "uart_api.h"
#define BUFFER_SIZE 1024
#define TARGET_COM_PORT 1
int main(void)
{
int fd;
char buff[BUFFER_SIZE];
if((fd = open_port(TARGET_COM_PORT)) < 0) /* 打开串口 */
{
perror("open_port");
return 1;
}
printf("ok\n");
if(set_com_config(fd, 115200, 8, 'N', 1) < 0) /* 配置串口 */
{
perror("set_com_config");
return 1;
}
printf("wei\n");
do
//while(1)
{
memset(buff, 0, BUFFER_SIZE);
printf("start read\n");
if (read(fd, buff, BUFFER_SIZE) > 0)
//read(fd, buff, BUFFER_SIZE);
{
printf("The received words are : %s\n", buff);
printf("success\n");
}
// else
// {
// printf("receive not success");
//}
sleep(2);
} while(strncmp(buff, "quit", 4));
//while(1);
write(fd, "ok", 2);
printf("success ok\n");
close(fd);
return 0;
}
ping检查虚拟机与开发板的连通性
./wwcom_writer和./wwcom_reader分别执行即可实现字符串的写入和读取
思考题:
- 用的是什么方式控制多个客户端连接?
答:用多线程方式
- 线程在网络通信哪个阶段创建的?
答:在接收阶段
- 服务器和客户端的 IP 地址怎么配置?
答:都是配置服务器的 IP 地址
总结:
1、了解linux环境下串行程序设计的方法,设备文件系统的使用方法;
2、掌握终端的主要属性及设置方法;
3、了解主机与开发板的连接和通信(包括串口和网络连接、主机与开发板之间的文件传输)。