学习C高级(二十五)
网络基本概念
网络:计算机与计算机通过一些媒介(网线、串口线、无线等等)相连组成
信息共享 (通信)
互联网:internet 网网相连
因特网:Internet 众多互联网中的一个
网络协议(Protocol)
网络编程:本质上也是进程间通信,但是是跨电脑的进程间通信
因特网TCP/IP协议簇(或叫TCP/IP栈)
OSI七层模型
TCP/IP五层模型
应用层:完成具体网络服务
传输层:将到达计算机的数据交付给指定进程 TCP UDP SCTP
为了屏蔽不同操作系统表示进程的差异,采用统一表示法来表示不同的进程
用端口号为每个进程进行标识
端口号:两字节的无符号整型
0~1023:知名端口,这些端口已经指定专门的网络服务
>= 1024: 动态端口
网络层:通过Internet定位到目标计算机 核心协议:IP
Internet网上的每台电脑都有一个唯一的身份标示----IP地址
Ip地址表示方法:
-
四字节整型 — 编程用
-
点分十进制字符串:用点分隔的四个0~255数字组成的字符串
“192.168.1.16” -
域名:用点分隔字母组合
“www.baidu.com” .com .edu .org .netwww ftp email 。。。。----> 网络服务名 (一种网络服务对应一种app)
baidu -----> 公司或某组织的名称
.com .edu .org .net … -----> 表示组织或公司的性质
.cn .tw … ------>表示区域名DNS domain name server 域名服务器
gethostbyname
特殊的IP地址:127.0.0.1 ---- 本地环回
数据链路层:局域网内网口到网口的传输
MAC地址(物理地址):每个网口的身份标示
查看IP地址的命令:
Linux:ifconfig
windows:ipconfig /all
物理层:如何使用指定的媒介传输二进制位
socket----套接字
插头、插排、插孔统称叫Socket
插头:使用电压服务
插孔:提供电压服务
插排:提供插孔
每个socket都有一个唯一的标识 — socket描述符
网络应用编程本质:进程间通信
socket地址 = IP地址 + 端口号
TCP传输
客户端:使用服务 ---- 插头
服务端:提供服务 ----- 插排:插孔的容器
插孔:一个插孔对应一个客户端
TCP:Transfer Control Protocol
- 面向连接:数据传输前必须要建立好连接(插头要插到插孔里后)
- 可靠传输:发送方能明确知道发送的数据对方有没有收到
- 按序到达
公共函数:
1. socket 创建主动套接字
int socket(int domain,int type,int protocol) 功能:创建一个socket 参数: domain:指定使用的协议簇 AF_INET 因特网TCP/IP协议簇 type:指定传输方式 SOCK_STREAM :流式传输 SOCK_DGRAM :数据报传输 protocol: 指定传输协议, IPPROTO_CP ---- TCP IPPROTO_UDP ---- UDP IPPROTO_SCTP ---- SCTP 为0时,如果type是SOCK_STREAM则默认为TCP传输 SOCK_DGRAM则默认为UDP传输 返回值:成功返回一个描述符(socket描述符) 备注: socket默认创建的套接字是主动套接字 |
socket ----- 双工通信 :接收发送数据用的同一个描述符
设某socket中有M个字节,读走N个字节,还剩余M-N个字节
struct sockaddr_in //见man 7 ip
{
short sin_family;//AF_INET
unsigned short sin_port;//端口号
struct in_addr sin_addr;//四字节整型IP地址
};
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(7878);
inet_aton("192.168.1.16",&servaddr.sin_addr);
2. inet_aton
int inet_aton(const char *cp,struct in_addr *inp) 功能:将点分十进制字符串形式的IP地址转换四字节整型IP地址 参数: cp:值参数,指向空间存放着点分十进制字符串形式的ip地址 inp:结果参数,其指向空间用来存放四字节整型IP地址 返回值:成功非0,失败为0,一般忽略返回值直接调用 |
3. htonl、htons、ntohl、ntohs
h host n net l long s short
字节序:h主机字节序 n网络字节序
整型数在内存中的存放次序:
小端字节序(little-endian):数的低位放在低地址
大端字节序(big-endian):数的低位放在高地址
网络字节序:大端
uint32_t htonl(uint32_t hostlong)
uint32_t ntohl(uint32_t netlong)
uint16_t htons(uint16_t hostshort)
uint16_t ntohs(uint16_t netshort)
4. close
close用来关闭各种描述符
5. read、write
read用来接收数据,write用来发送数据
客户端专用函数:
1. connect 与服务器连接
int connnect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) 功能:通过三路握手完成与服务器的连接 隐藏功能:该函数会给客户端socket填好自己IP地址和端口号 参数: sockfd:发起连接的客户端socket描述符(插头) addr: 指向空间存放着服务端地址(包含IP地址+端口号) addrlen:指出addr指向空间的字节数 返回值:成功为0,失败-1 备注: struct sockaddr --- 通用地址结构 struct sockaddr_in --- 因特网专用地址结构 ----因特网编程时用 |
ret = connnect(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(ret < 0)
{//出错处理
}
服务端专用函数:
1. bind 给服务器指定自身的IP地址和端口号
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) 功能:给服务器指定自身的IP地址和端口号 参数: sockfd:服务器socket描述符(插排) addr: 指向空间存放着服务端地址(包含IP地址+端口号) addrlen:指出addr指向空间的字节数 返回值:成功为0,失败-1 |
2. listen 主动套接字为被动套接字
int listen(int sockfd,int backlog) 功能: 变主动套接字为被动套接字(变插头为插排), 并通知该被动套接字(插排)可以开始处理客户端的连接请求 参数: sockfd:服务器socket描述符(插排)(此后该套接字专门用来处理连接) backlog:5~20 返回值:成功为0,失败-1 备注:该函数不会引起阻塞 |
而和对应客户端进行数据交互的socket是另外的socket描述符(插孔)
3. accept 与客户端连接
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen)
功能:
如果没有客户端和服务器建立好连接该函数阻塞;
一旦有一个客户端和服务器建立好了连接,
则该函数返回可以与该客户端进行数据交互用的描述符(插孔)
参数:
sockfd:被动套接字的描述符(插排)
addr:结果参数,其指向空间用来存放此次连接成功的客户端的IP地址和端口号
addrlen:值-结果参数
特殊ip地址:127.0.0.1 本地环回
客户端与服务器
客户端与服务器的连接
客户端与服务器连接后,简单的数据发送和接收:客户端发送hello,服务端接收数据后打印,服务器发送hello,客户端接收数据后打印。
//服务器 server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int servfd = -1;
int ret = 0;
struct sockaddr_in servaddr;
int datafd = -1;
char buf[8] = "";
/*①socket 得到连接用的描述符,创建一个主动套接字(插头)*/
servfd = socket(AF_INET,SOCK_STREAM,0);
/*填写服务端自己的IP地址和端口号*/
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9898);
inet_aton("127.0.0.1",&servaddr.sin_addr);
/*②bind 为指定描述符填写本端的IP地址和端口号*/
ret = bind(servfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
/*③listen 将主动套接字转换为被动套接字(插头----->插排)*/
ret += listen(servfd,6);
if(ret < 0)
{
perror("bind or listen error");
return 1;
}
while(1)
{
/*④accept 得到数据交互用的描述符
检查是否有客户端与服务器连接建立成功
如果有则返回与该客户端进行数据交互用的描述符(插孔)
如果没有则accept阻塞等待
*/
datafd = accept(servfd,NULL,NULL);
if(datafd < 0)
{//accpet出错
if(errno == EINTR)
{//信号引起的错误可以忽略
continue;
}
else
{//accept真正出错,意味着服务端已经不能正常提供相应服务
perror("accept error");
break;
}
}
/*read 接收数据*/
read(datafd,buf,8);
printf("Client Say to me:%s\n",buf);
/*write 发送数据*/
write(datafd,"hello",6);
/*close 不再为对应客户端提供服务了,应及时关闭数据交互用描述符*/
close(datafd);
datafd = -1;
}//end while(1)
/*close 服务端程序退出前关闭建立连接用的描述符*/
close(servfd);
servfd = -1;
return 0;
}
//客户端 client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int fd = -1;
struct sockaddr_in servaddr;
int ret = 0;
char buf[8] = "";
/*①socket 得到连接用的描述符,创建一个主动套接字(插头)*/
fd = socket(AF_INET,SOCK_STREAM,0);
/*填写服务端的IP地址和端口号*/
bzero(&servaddr,sizeof(servaddr));//memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(9898);
inet_aton("127.0.0.1",&servaddr.sin_addr);
/*②connect通过三路握手与服务器建立连接*/
ret = connect(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(ret < 0)
{//连接失败,无法使用描述符收发数据
perror("connect error");
return 1;
}
/*发送数据*/
write(fd,"hello",6);
/*接收数据*/
read(fd,buf,8);
printf("Server say to me:%s\n",buf);
/*close 及时关闭连接描述符*/
close(fd);
fd = -1;
return 0;
}
先打开服务器,后打开客户端
客户端与服务器的数据交互
客户端接收用户输入的整数n,并产生n个随机数(1000以内)发送给服务器,服务器求出n个随机数的平均值发送回给客户端
//服务器和客户端的头文件 randpdu.h
#ifndef RAND_PDU_H
#define RAND_PDU_H
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct RandPDU
{
int count;
char buf[1];
};
#endif
//服务器 server.c
#include "randpdu.h"
int CreateServerSocket(char *ip,unsigned short port);
struct RandPDU *ReceiveRandPDU(int datafd);
float GetAve(int *pi,int count);
int main(int argc,char *argv[])
{
int port = 0;
int fd = -1;
struct RandPDU *pstPDU = NULL;
float ave = 0.0f;
int datafd = -1;
int ret = 0;
//argv[1]为服务器IP,argv[2]为服务器端口号
if(argc < 3)
{
printf("argument too few\n");
return 1;
}
sscanf(argv[2],"%d",&port);
//检查端口是否正确
if(port <= 0 || port > 65535)
{
printf("port is invalid\n");
return 2;
}
//得到连接用的描述符
fd = CreateServerSocket(argv[1],(unsigned short)port);
if(fd < 0)
{
return 3;
}
while(1)
{ //得到数据交互的描述符
datafd = accept(fd,NULL,NULL);
if(datafd < 0)
{
if(errno == EINTR)
{
continue;
}
else
{
perror("accept failed");
break;
}
}
//接收客户端数据并且处理数据
pstPDU = ReceiveRandPDU(datafd);
if(NULL == pstPDU)
{
close(datafd);
datafd = -1;
continue;
}
ave = GetAve((int *)pstPDU->buf,pstPDU->count);
free(pstPDU);
pstPDU = NULL;
//把处理结果发送给客户端
ret = write(datafd,&ave,sizeof(ave));
if(ret != sizeof(ave))
{
perror("Send Ave Error");
}
//关闭数据交互描述符
close(datafd);
datafd = -1;
}
//关闭连接描述符
close(fd);
fd = -1;
return 0;
}
/*计算随机数的平均值*/
float GetAve(int *pi,int count)
{
float sum = 0.0;
int i = 0;
for(i = 0;i < count;i++)
{
sum += *(pi + i);
}
return sum / count;
}
/*产生随机数*/
struct RandPDU *ReceiveRandPDU(int datafd)
{
int count = 0;
int ret = 0;
struct RandPDU *pstPDU = NULL;
ret = read(datafd,&count,sizeof(int));
if(ret != sizeof(int))
{
perror("read rand count failed");
return NULL;
}
if(count <= 0)
{
printf("Rand Count is invalid\n");
return NULL;
}
pstPDU = (struct RandPDU *)malloc((count+1) * sizeof(int));
if(NULL == pstPDU)
{
perror("Malloc Failed");
return NULL;
}
memset(pstPDU,0,(count + 1) * sizeof(int));
pstPDU->count = count;
ret = read(datafd,pstPDU->buf,pstPDU->count * sizeof(int));
if(ret != pstPDU->count * sizeof(int))
{
perror("Receive Rand Data Failed");
free(pstPDU);
pstPDU = NULL;
return NULL;
}
return pstPDU;
}
/*创建套接字*/
int CreateServerSocket(char *ip,unsigned short port)
{
int fd = -1;
int ret = 0;
struct sockaddr_in servaddr;
//①socket
fd = socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
inet_aton(ip,&servaddr.sin_addr);
//②bind
ret = bind(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
//③listen
ret += listen(fd,6);
if(ret < 0)
{
close(fd);
fd = -1;
perror("bind or listen error");
return -1;
}
return fd;
}
//客户端 client.c
#include "randpdu.h"
int CreateClientSocket(char *ip,unsigned short port);
int InputNumber();
int main(int argc,char *argv[])
{
int port = 0;
int fd = -1;
int count = 0;
struct RandPDU *pstPDU = NULL;
int i = 0;
float ave = 0.0f;
int ret = 0;
if(argc < 3)
{
printf("argument too few\n");
return 1;
}
sscanf(argv[2],"%d",&port);
if(port <= 0 || port > 65535)
{
printf("port is invalid\n");
return 2;
}
srand(time(NULL));
//得到连接用的描述符
fd = CreateClientSocket(argv[1],(unsigned short)port);
if(fd < 0)
{
return 3;
}
do
{
count = InputNumber();
if(count <= 0)
{
printf("Your input error\n");
break;
}
pstPDU = (struct RandPDU *)malloc((count+1) * sizeof(int));
if(NULL == pstPDU)
{
perror("Malloc Failed");
break;
}
memset(pstPDU,0,(count + 1) * sizeof(int));
pstPDU->count = count;
for(i = 0;i < count;i++)
{
*((int *)pstPDU->buf + i) = rand() % 1000;
}
//发送协议数据给服务器
ret = write(fd,pstPDU,(pstPDU->count + 1) * sizeof(int));
if(ret != (pstPDU->count + 1) * sizeof(int))
{
perror("Send Rand Failed");
break;
}
//接收服务器的协议数据
ret = read(fd,&ave,sizeof(float));
if(ret != sizeof(float))
{
perror("Receive Ave Failed\n");
break;
}
printf("The avrange is %.2f\n",ave);
}while(0);
if(pstPDU != NULL)
{
free(pstPDU);
pstPDU = NULL;
}
//关闭连接描述符
close(fd);
fd = -1;
return 0;
}
int InputNumber()
{
int num = 0;
printf("Please input a count:\n");
scanf("%d",&num);
while(getchar() != '\n')
{
}
return num;
}
int CreateClientSocket(char *ip,unsigned short port)
{
int fd = -1;
int ret = 0;
struct sockaddr_in servaddr;
//①socket
fd = socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
inet_aton(ip,&servaddr.sin_addr);
//②connect
ret = connect(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(ret < 0)
{
close(fd);
fd = -1;
perror("connect failed");
return -1;
}
return fd;
}
先打开服务器,后打开客户端
简单的文件传输:
客户端向服务器发送文件名,
服务器将文件内容发送给客户端,客户端收到文件内容在./clientfile目录下创建一个同名文件,并将收到的内容保存该文件中
//filepdu.h
#ifndef FILE_PDU_H
#define FILE_PDU_H
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CLIENT_PATH "./clientfile/"
struct FilePDU
{
int len;
char buf[1];
};
struct FilePDU *CreateFilePDU(int len,void *pdata);
int DestroyFilePDU(struct FilePDU *pstPDU);
struct FilePDU *ReceiveFilePDU(int fd);
int MyGetString(char arr[],int size);
#endif
//filepdu.c
#include "filepdu.h"
//创建PDU
struct FilePDU *CreateFilePDU(int len,void *pdata)
{
struct FilePDU *pstPDU = NULL;
int tlen = sizeof(int);
if(len > 0)
{
tlen += len;
}
pstPDU = (struct FilePDU *)malloc(tlen);
if(NULL == pstPDU)
{
perror("Malloc PDU Failed");
return NULL;
}
memset(pstPDU,0,tlen);
pstPDU->len = len;
if(len > 0 && pdata != NULL)
{
memcpy(pstPDU->buf,pdata,len);
}
return pstPDU;
}
//销毁PDU
int DestroyFilePDU(struct FilePDU *pstPDU)
{
if(pstPDU != NULL)
{
free(pstPDU);
pstPDU = NULL;
}
return 0;
}
//接收PDU
struct FilePDU *ReceiveFilePDU(int fd)
{
int len = 0;
int tlen = 0;
int ret = 0;
struct FilePDU *pstPDU = NULL;
ret = read(fd,&len,sizeof(int));
if(ret != sizeof(int))
{
perror("Receive PDU Len Failed");
return NULL;
}
tlen = sizeof(int);
if(len > 0)
{
tlen += len;
}
pstPDU = (struct FilePDU *)malloc(tlen);
if(NULL == pstPDU)
{
perror("Malloc RecvPDU Failed");
return NULL;
}
memset(pstPDU,0,tlen);
pstPDU->len = len;
if(len > 0)
{
ret = read(fd,pstPDU->buf,len);
if(ret != len)
{
perror("Receive PDU Data Failed");
free(pstPDU);
pstPDU = NULL;
return NULL;
}
}
return pstPDU;
}
#include "filepdu.h"
int CreateClientSocket(const char *ip,unsigned short port);
int SaveResult(struct FilePDU *pstRet,const char *filename);
int main(int argc,char *argv[])
{
int port = 0;
int fd = -1;
char filename[64] = "";
struct FilePDU *pstPDU = NULL;
struct FilePDU *pstRet = NULL;
int ret = 0;
//argv[1]为服务器IP,argv[2]为服务器端口
if(argc < 3)
{
printf("argument too few\n");
return 1;
}
sscanf(argv[2],"%d",&port);
if(port <= 0 || port > 65535)
{
printf("port is invalid\n");
return 2;
}
//检查文件夹是否存在,不存在就创建文件夹
ret = access(CLIENT_PATH,F_OK);
if(ret != 0)
{
ret = mkdir(CLIENT_PATH,0777);
if(ret < 0)
{
perror("mkdir failed");
return 3;
}
}
//创建socket,得到数据交换的描述符
fd = CreateClientSocket(argv[1],(unsigned short)port);
if(fd < 0)
{//创建socket失败退出进程
return 3;
}
do
{
printf("Please input a file name:\n");
MyGetString(filename,64);
//创建pstPDU
pstPDU = CreateFilePDU(strlen(filename)+1,filename);
if(NULL == pstPDU)
{
break;
}
//发送pstPDU
ret = write(fd,pstPDU,pstPDU->len+sizeof(int));
if(ret != pstPDU->len + sizeof(int))
{
perror("Send Filename Failed");
break;
}
//销毁pstPDU
DestroyFilePDU(pstPDU);
pstPDU = NULL;
//接收pstRet
pstRet = ReceiveFilePDU(fd);
if(NULL == pstRet)
{
break;
}
//保存pstRet
SaveResult(pstRet,filename);
//销毁pstRet
DestroyFilePDU(pstRet);
pstRet = NULL;
}while(0);
//关闭数据交换的描述符
close(fd);
fd = -1;
return 0;
}
//保存PDU
int SaveResult(struct FilePDU *pstRet,const char *filename)
{
char *pathname = NULL;
int pnlen = 0;
int fd = -1;
int ret = 0;
//创建pathneme堆空间用来存放文件路径
if(pstRet->len <= 0)
{
printf("file from server is no content len=%d\n",pstRet->len);
return -1;
}
pnlen = strlen(CLIENT_PATH) + strlen(filename) + 1;
pathname = (char *)malloc(pnlen);
if(NULL == pathname)
{
perror("malloc pathname failed");
return -1;
}
memset(pathname,0,pnlen);
//组合文件路径存放到pathname堆空间
strcpy(pathname,CLIENT_PATH);
strcat(pathname,filename);
//打开pathname所指的文件,得到文件描述符
fd = open(pathname,O_WRONLY | O_TRUNC | O_CREAT,0666);
if(fd < 0)
{
perror("open failed");
printf("open %s file failed\n",pathname);
free(pathname);
pathname = NULL;
return -1;
}
//释放pathname堆空间
free(pathname);
pathname = NULL;
//把接收的的pstRet写进文件中
ret = write(fd,pstRet->buf,pstRet->len);
//关闭文件描述符
close(fd);
fd = -1;
if(ret != pstRet->len)
{
perror("Save File Data Failed");
return -1;
}
return 0;
}
int CreateClientSocket(const char *ip,unsigned short port)
{
int fd = -1;
struct sockaddr_in servaddr;
int ret = 0;
//创建插头
fd = socket(AF_INET,SOCK_STREAM,0);
//填写服务器的IP和端口号
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
inet_aton(ip,&servaddr.sin_addr);
//自动填好客户端IP和端口。插头寻找插孔
ret = connect(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(ret < 0)
{//寻找插孔失败
perror("connect server failed");
close(fd);
fd = -1;
}
return fd;//寻找插孔成功,返回数据交换描述符
}
int MyGetString(char arr[],int size)
{
int len = 0;
fgets(arr,size,stdin);
len = strlen(arr);
if(arr[len-1] == '\n')
{
arr[len-1] = '\0';
}
else
{
while(getchar() != '\n')
{
}
}
return 0;
}
#include "filepdu.h"
int CreateServerSocket(const char *ip,unsigned short port);
int HandleClient(int datafd);
int main(int argc,char *argv[])
{
int port = 0;
int fd = -1;
int datafd = -1;
struct sockaddr_in cliaddr;
socklen_t addrlen = 0;
if(argc < 3)
{
printf("argument too few\n");
return 1;
}
sscanf(argv[2],"%d",&port);
if(port <= 0 || port > 65535)
{
printf("port is invalid\n");
return 2;
}
//得到连接描述符,
fd = CreateServerSocket(argv[1],(unsigned short)port);//插排
if(fd < 0)
{
return 3;
}
while(1)
{ //得到数据交换描述符
addrlen = sizeof(cliaddr);
datafd = accept(fd,(struct sockaddr *)&cliaddr,&addrlen);//插排提供--->插孔等待插头的插入
if(datafd < 0)
{
if(errno == EINTR)
{
continue;
}
else
{
perror("accept error");
break;
}
}
printf("Client port is %d\n",ntohs(cliaddr.sin_port));
printf("Client IP is %s\n",inet_ntoa(cliaddr.sin_addr));
//处理客户端的PDU数据
HandleClient(datafd);
//关闭数据交换描述符
close(datafd);
datafd = -1;
}
//关闭连接描述符
close(fd);
fd = -1;
return 0;
}
//处理客户端的PDU数据
int HandleClient(int datafd)
{
struct FilePDU *pstPDU = NULL;
struct FilePDU *pstRsp = NULL;
int ret = 0;
int filefd = -1;
int len = 0;
int sendlen = 0;
struct stat meta;
/*接收客户端发来的文件名*/
pstPDU = ReceiveFilePDU(datafd);
if(NULL == pstPDU)
{
return -1;
}
/*决定发给客户端的PDU有没有数据部分 len <= 0为无数据*/
ret = access(pstPDU->buf,F_OK);
if(ret != 0)
{
len = -1;
}
else
{
stat(pstPDU->buf,&meta);
if((meta.st_mode & S_IFMT) != S_IFREG)
{
len = -2;
}
else
{
len = meta.st_size;
if(len <= 0)
{
len = 0;
}
else
{
filefd = open(pstPDU->buf,O_RDONLY);
if(filefd < 0)
{
len = -3;
}
}//文件内容的长度是不是大于零
}//是不是普通文件
}//文件是否存在
DestroyFilePDU(pstPDU);
pstPDU = NULL;
/*根据len值创建给客户端发送的PDU*/
if(len <= 0)
{
pstRsp = CreateFilePDU(len,NULL);
}
else
{
/*准备读文件内容所需的内存空间*/
char *pfiledata = NULL;
pfiledata = (char *)malloc(len);
if(NULL == pfiledata)
{
perror("Malloc File Data Failed");
len = -4;
pstRsp = CreateFilePDU(len,NULL);
}
else
{//空间分配成功则读文件内容到动态空间
memset(pfiledata,0,len);
ret = read(filefd,pfiledata,len);
if(ret != len)
{
perror("read file data failed");
len = -5;
pstRsp = CreateFilePDU(len,NULL);
}
else
{
pstRsp = CreateFilePDU(len,pfiledata);
}
free(pfiledata);
pfiledata = NULL;
}
close(filefd);
filefd = -1;
}//len > 0
/*发送PDU给客户端*/
if(len <= 0)
{
sendlen = sizeof(int);
}
else
{
sendlen = sizeof(int) + len;
}
if(pstRsp != NULL)
{
ret = write(datafd,pstRsp,sendlen);
DestroyFilePDU(pstRsp);
pstRsp = NULL;
if(ret != sendlen)
{
perror("Send Response Error");
return -1;
}
}
return 0;
}
int CreateServerSocket(const char *ip,unsigned short port)
{
int fd = -1;
struct sockaddr_in servaddr;
int ret = 0;
//插头
fd = socket(AF_INET,SOCK_STREAM,0);
//填写服务器IP和端口号
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
inet_aton(ip,&servaddr.sin_addr);
ret = bind(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
//插头成为--->插排
ret += listen(fd,6);
if(ret < 0)
{
perror("bind or listen error");
close(fd);
fd = -1;
}
return fd;
}
先打开服务器,填写服务器IP和端口号,等待客户端的连接。
打开客户端,填写服务器IP和端口号,与服务器连接,连接成功服务器得到客户端的IP和客户端。
在客户端输入文件名,服务器将文件内容发送给客户端,客户端收到文件内容在./clientfile目录下创建一个同名文件,并将收到的内容保存该文件中。