学习C高级(二十五)

网络基本概念

网络:计算机与计算机通过一些媒介(网线、串口线、无线等等)相连组成
信息共享 (通信)
互联网:internet 网网相连
因特网:Internet 众多互联网中的一个
网络协议(Protocol)
网络编程:本质上也是进程间通信,但是是跨电脑的进程间通信

因特网TCP/IP协议簇(或叫TCP/IP栈)

OSI七层模型

TCP/IP五层模型
应用层:完成具体网络服务

传输层:将到达计算机的数据交付给指定进程 TCP UDP SCTP
  为了屏蔽不同操作系统表示进程的差异,采用统一表示法来表示不同的进程
  用端口号为每个进程进行标识
  端口号:两字节的无符号整型
    0~1023:知名端口,这些端口已经指定专门的网络服务
    >= 1024: 动态端口

网络层:通过Internet定位到目标计算机 核心协议:IP
  Internet网上的每台电脑都有一个唯一的身份标示----IP地址

Ip地址表示方法:

  1. 四字节整型 — 编程用

  2. 点分十进制字符串:用点分隔的四个0~255数字组成的字符串
    “192.168.1.16”

  3. 域名:用点分隔字母组合
    “www.baidu.com” .com .edu .org .net

    www 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. 面向连接:数据传输前必须要建立好连接(插头要插到插孔里后)
  2. 可靠传输:发送方能明确知道发送的数据对方有没有收到
  3. 按序到达

公共函数:

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目录下创建一个同名文件,并将收到的内容保存该文件中。
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值