目录:
1. readme.txt, 该文件描述了各个文件的代码的功能
2. .info文件, 配置文件, 存放了一些IP和port
3. TCP.h文件
4. INIT.c文件
5. server.c文件
6.client.cw文件
7.Makefile
8.调试调出来的经验总结
一.readme.txt
1. .info文件负责存放一些配置信息
2. TCP.h文件包含一些头文件, 定义的一些数据类型, 一些函数指针和声明的几个函数
3. INIT.c实现了TCP.h文件中声明的那些函数
4. server.c服务端代码
5. client.c客户端代码
6. Makefile ,对代码编译, 简化操作的
二. .info
127.0.0.1
8888
三. TCP.h
/*防止头文件被重复的包含*/
#ifndef _TCP_
#define _TCP_
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include<stdbool.h>
#include<errno.h>
#include <sys/stat.h>
#include <fcntl.h>
/*定义出错处理的宏*/
#define ERR_EXIT(msg) do{perror(msg);exit(EXIT_FAILURE);}while(0)
#define BACKLOG 10
#define CONFIGFILE ".info"
#define OK 1
#define CONFIG_SIZE 20
/*定义一些数据类型和函数指针*/
typedef struct sockaddr_in Addr_in;
typedef struct sockaddr Addr;
typedef int (*Fun_p1)(int, struct sockaddr *, socklen_t);
typedef ssize_t (*Fun_p2)(int, void *, size_t, int);
/*声明几个函数*/
void Argument_check(int argc, const char *argv[]);
int SockInit(const char *argv[], bool flags);
void InintConfig(const char *argv[]);
int DataHandler(int fd, void *buf, int data_size, Fun_p2 fp);
#endif
四. INIT.c文件
#include"TCP.h"
/*@brife:检查参数
* @para: main function param
*@return: None
*/
void Argument_check(int argc, const char *argv[]) {
if(argc < 3) {
printf("%s a few argument <addr><port>\n", argv[0]);
exit(0);
}
}
/*准备套接字, 使用标志位区分执行服务端和客户端的代码*/
int SockInit(const char *argv[], bool flags) {
int sockfd;
Fun_p1 fp = flags?bind:connect;
Addr_in addr;
socklen_t addrlen = sizeof(addr);
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
ERR_EXIT("socket");
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
if(!(inet_aton(argv[1], &addr.sin_addr)))
ERR_EXIT("inet_aton");
fp(sockfd, (Addr *)&addr, addrlen);
if(flags) {
int flags = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(int));
if((listen(sockfd, BACKLOG)) < 0)
ERR_EXIT("listen");
}
return sockfd;
}
/*从配置文件中读出IP和port*/
void InintConfig(const char *argv[]) {
printf("[%s %d]\n", __FUNCTION__, __LINE__);
FILE *fp;
char buf[CONFIG_SIZE];
if((fp = fopen(CONFIGFILE, "r")) == NULL)
ERR_EXIT("fopen");
fgets(buf, 20, fp);
int len = strlen(buf);
buf[len - 1] = '\0';
argv[1] = buf;
fgets(buf, 20, fp);
len = strlen(buf);
buf[len - 1] = '\0'; //去掉回车
argv[2] = buf;
return ;
}
/*数据处理*/
int DataHandler(int fd, void *buf, int data_size, Fun_p2 fp) {
int ret;
char *str =fp==recv? "recv":"send";
do{
ret = fp(fd, buf, data_size, 0);
}while(ret < 0 && errno == EINTR); //因为信号问题而产生的中断
if(ret < 0)
ERR_EXIT(str);
return ret;
}
五. server.c(服务端)
#include"TCP.h"
int main(int argc, const char *argv[])
{
int fd, newfd, file_fd;
int ret;
char buf[BUFSIZ] = {};
Addr_in peeraddr;
socklen_t peeraddrlen = sizeof(Addr_in);
/*检查参数*/
Argument_check(argc, argv);
/*准备套接字*/
fd = SockInit(argv, true);
do{
newfd = accept(fd, (Addr *)&peeraddr, &peeraddrlen);
}while(newfd < 0 && errno == EINTR);
if(newfd < 0)
ERR_EXIT("accept line 13");
printf("[%s %d]:connect succes.\n", inet_ntoa(peeraddr.sin_addr), \
ntohs(peeraddr.sin_port));
/*接收客户端发来的文件名*/
ret = DataHandler(newfd, buf, BUFSIZ, recv);
/*在服务端目录下创建该文件*/
if((file_fd = open(buf, O_WRONLY|O_CREAT, 0666)) < 0)
ERR_EXIT("open line 19");
/*给客户端发送确认的信息, 给客户端一个何时发送数据的判断依据*/
buf[0] = OK;
DataHandler(newfd, buf, 1, (Fun_p2)send);
/*循环处理数据*/
while(1) {
ret = DataHandler(newfd, buf, BUFSIZ, recv);
if(ret < 0) {
ERR_EXIT("DataHandler");
} else if(ret == 0) {
printf("[%s %d]:disconnect succes.\n", inet_ntoa(peeraddr.sin_addr), \
ntohs(peeraddr.sin_port));
break;
}
/*将接收到缓存区里的数据写到创建的文件中去*/
write(file_fd, buf, ret);
}
close(newfd);
close(fd);
close(file_fd);
return 0;
}
六. client.cw文件
#include"TCP.h"
int main(int argc, const char *argv[])
{
int fd, file_fd;
int ret;
char buf[BUFSIZ] = {};
char *filename = (char *)argv[1];
FILE *fp;
/*打开配置文件, 读出IP和port给主函数参数*/
if((fp = fopen(".info", "r")) == NULL)
ERR_EXIT("fopen");
fgets(buf, 20, fp);
buf[strlen(buf) - 1] = '\0';
argv[1] = buf;
fgets(&buf[20], 20, fp);
buf[strlen(&buf[20]) - 1 + 20] = '\0';
argv[2] = &buf[20];
/*准备套接字*/
fd = SockInit(argv, false);
/*发送文件名*/
DataHandler(fd, filename, strlen(filename), (Fun_p2)send);
/*接收确认信号*/
DataHandler(fd, buf, 1, recv);
/*打开要发送的文件*/
file_fd = open(filename, O_RDONLY);
if(file_fd < 0)
ERR_EXIT("open line 16");
if(buf[0] == OK) {
do{
ret = read(file_fd, buf, BUFSIZ);
}while(ret < 0 && errno == EINTR);
if(ret < 0)
ERR_EXIT("read");
if(ret > 0) {
DataHandler(fd, buf, ret, (Fun_p2)send);
} else if(!ret) {
printf("over\n");
exit(0);
}
}
close(file_fd);
close(fd);
return 0;
}
7.Makefile
CC=gcc
CFLAGS=-g -Wall
all:server client
server:server.c INIT.c
client:client.c INIT.c
clean:
rm server client
mv:
mv ./client .info /tmp/
八. 调试调出来的经验总结
1.打开一个文件的时候注意文件的权限,注意区分读写权限的使用场景。
2.编译出现missing terminaing character缺失终止字符, 原因可能是输入了中文的符号或者代码出现换行没加\。
3. 注意写入文件和如读出文件的触发条件。
4.fgets函数使用的很多小坑, 对缓冲区的地址偏移的计算。