1.select搭建TCP服务器
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IP "192.168.8.190"
#define PROT 7777
#define ERR(err) \
do \
{ \
fprintf(stderr, "errline:%d\n", __LINE__); \
perror(err); \
} while (0)
int main(int argc, const char *argv)
{
//创建套接字
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
ERR("socket");
return -1;
}
//允许端口快速重用
int reuse = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
{
ERR("setsockopt");
return -1;
}
//绑定套接字
struct sockaddr_in sim, add; //这个是使用的真实的结构体 bind函数中的结构体只是为了防止报警告
sim.sin_family = AF_INET;
sim.sin_addr.s_addr = inet_addr(IP);
sim.sin_port = htons(PROT);
if (bind(fd, (struct sockaddr *)&sim, sizeof(sim)) < 0)
{
ERR("bind");
return -1;
}
//设置监听
if (listen(fd, 128) < 0)
{
ERR("listen");
return -1;
}
int fs = 0;
//创建一个集合
fd_set readfds, tempfds;
//初始化
FD_ZERO(&readfds);
FD_ZERO(&tempfds);
//将文件描述符添加进去
FD_SET(0, &readfds);
FD_SET(fd, &readfds);
//储存添加的最大的文件描述符
int max = fd;
int temps; //
char str[128] = "";
char rec[128] = "";
ssize_t res = 0;
socklen_t addrlen = sizeof(add);
while (1)
{
tempfds = readfds;
if (select(max + 1, &tempfds, NULL, NULL, NULL) < 0)
{
ERR("select");
return -1;
}
for (int i = 0; i < max + 1; i++)
{
if (FD_ISSET(i, &tempfds) == 0)
{
continue; //如果集合中没有对应的,继续找
}
if (0 == i)
{
//如果等于0 就是fgets
fgets(str,sizeof(str),stdin);
str[strlen(str)-1]=0;
printf("输入的:");
}
else if (fd == i)
{
//如果等于fd,代表accept
printf("waiting......\n");
fs = accept(fd, (struct sockaddr *)&add, &addrlen); // fd是用于accept的如果想打印数据还需要新的文件描述符,发送数据用的是fs
if (fs < 0)
{
ERR("accept");
return -1;
}
printf("客户端连接\n");
FD_SET(fs,&readfds);
max = fd>fs?fd:fs;//更新最大值
}
else
{
//剩下的就是发送的接收变量
bzero(rec,sizeof(rec));
res = recv(i,rec,sizeof(rec),0);
if(res<0)
{
ERR("recv");
}
else if(0 == res)
{
printf("客户端断开连接\n");
//需要处理断开连接后的文件描述符
close(i);//关闭
FD_CLR(i,&readfds);//在集合中删除
//删除之后出现最大值的问题,如果删除的是最大的那个需要找到下一个最大的值
for(int j=max;j>0;j--)
{
if(FD_ISSET(j,&readfds))
{
max = j;
break;
}
}
}
else
{
printf("%s\n",rec);
}
}
}
}
//获取连接成功的套接字,在所有连接的队列头中选一个连接,生成新的文件描述符用于通信
//接收recv
//IP地址记录用链表实现
if (close(fd) || close(fs))
{
ERR("close");
return -1;
}
return 0;
}
2.select搭建TCP客户端
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IP "192.168.8.190"
#define PORT 7777
#define ERR(err) \
do \
{ \
fprintf(stderr, "errline:%d\n", __LINE__); \
perror(err); \
} while (0)
int main(int argc, const char *argv[])
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
ERR("socket");
return -1;
}
struct sockaddr_in add;
add.sin_family = AF_INET;
add.sin_port = htons(PORT);
add.sin_addr.s_addr = inet_addr(IP);
if (connect(fd, (struct sockaddr *)&add, sizeof(add)) < 0)
{
ERR("connect");
return -1;
}
fd_set readfds, tempfds;
FD_ZERO(&readfds);
FD_ZERO(&tempfds);
FD_SET(0, &readfds);
FD_SET(fd, &readfds);
int max = fd;
char sendbuff[128] = "";
char recvbuff[128] = "";
while (1)
{
tempfds = readfds;
if(select(max+1,&tempfds,NULL,NULL,NULL)<0)
{
ERR("select");
return -1;
}
for(int i=0;i<max+1;i++)
{
if(0 == FD_ISSET(i,&tempfds))
{
continue;
}
if(0 == i)
{
bzero(sendbuff,sizeof(sendbuff));
fgets(sendbuff,sizeof(sendbuff),stdin);
sendbuff[strlen(sendbuff)-1];
if(send(fd,sendbuff,sizeof(sendbuff),0)<0)
{
ERR("send");
return -1;
}
}
else if(fd == i)
{
bzero(recvbuff,sizeof(recvbuff));
if(recv(fd,recvbuff,sizeof(recvbuff),0)<0)
{
ERR("recv");
return -1;
}
printf("%s\n",recvbuff);
}
}
}
close(fd);
return 0;
}
3.poll搭建TCP客户端
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IP "192.168.8.190"
#define PORT 7777
#define ERR(err) \
do \
{ \
fprintf(stderr, "errline:%d\n", __LINE__); \
perror(err); \
} while (0)
int main(int argc, const char *argv)
{
//创建套接字
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
ERR("socket");
return -1;
}
struct sockaddr_in add;
add.sin_family = AF_INET;
add.sin_port = htons(PORT);
add.sin_addr.s_addr = inet_addr(IP);
if (connect(fd, (struct sockaddr *)&add, sizeof(add)) < 0)
{
ERR("connect");
return -1;
}
//创建一个事件集合
struct pollfd akg[2];
akg[0].fd = 0; //用于监测stdin即终端输入
akg[0].events = POLLIN; //监测读事件,需要手动添加
akg[1].fd = fd; //监测套接字文件描述符
akg[1].events = POLLIN; //监测读事件
char sendbuff[128] = "";
char recvbuff[128] = "";
ssize_t res = 0;
while (1)
{
int pds = poll(akg, 2, -1);
if (pds < 0)
{
ERR("poll");
return -1;
}
else if (0 == pds)
{
printf("服务器异常\n");
return -1;
}
if (akg[0].revents & POLLIN)
{
bzero(sendbuff,sizeof(sendbuff));
fgets(sendbuff,sizeof(sendbuff),stdin);
sendbuff[strlen(sendbuff)-1];
if(send(fd,sendbuff,sizeof(sendbuff),0)<0)
{
ERR("send");
return -1;
}
}
if(akg[1].events&POLLIN)
{
bzero(recvbuff,sizeof(recvbuff));
res = recv(fd,recvbuff,sizeof(recvbuff),0);
if(res<0)
{
ERR("recv");
return -1;
}
else if(0 == res)
{
printf("服务器断开连接\n");
break;
}
printf("%s\n",recvbuff);
}
}
if(close(fd)<0)
{
ERR("close");
return -1;
}
}
4.基于TCP的文件服务器以及客户端
服务器:
fun.h
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define N 64
#define M 64
#define LICENCE "201810"
#define IP "192.168.8.190"
#define PORT 8888
#define ERR(err) \
do \
{ \
fprintf(stderr, "errline:%d\n", __LINE__); \
perror(err); \
} while (0)
typedef struct mas
{
int fs;
char recvbuff[128]; //储存接收的数据判断请求
} info;
int init();
void *callback(void *arg);
int UserSignIn(int fd);
int Register(int fd); //用户注册
int File_Info(int fd,char *fox);
int RecvFile(int fd, char *fox);
int SendFile(int fd, char *recvbuff);
int Exit(int fd);
func.c
#include "func.h"
int init()
{
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
ERR("socket");
return -1;
}
//允许端口快速重用
int value;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0)
{
ERR("setsockopt");
return -1;
}
struct sockaddr_in add;
add.sin_family = AF_INET;
add.sin_addr.s_addr = inet_addr(IP);
add.sin_port = htons(PORT);
if (bind(fd, (struct sockaddr *)&add, sizeof(add)) < 0)
{
ERR("bind");
return -1;
}
if (listen(fd, 128) < 0)
{
ERR("listen");
return -1;
}
return fd;
}
void *callback(void *arg)
{
pthread_detach(pthread_self());
char recvbuff[128] = "";
short temp = 0;
ssize_t res = 0;
int flag = 1;
int fd = ((info *)arg)->fs; //用于通信
while (flag)
{
res = recv(fd, recvbuff, sizeof(recvbuff), 0);
if (0 == res)
{
printf("客户端退出\n");
return NULL;
}
temp = ntohs(*(short *)recvbuff);
switch (temp)
{
case 1:
SendFile(fd, recvbuff);
break; //发送文件
case 2:
RecvFile(fd, recvbuff);
break; //接收文件
case 3:
Register(fd);
break; //注册
case 4:
File_Info(fd, recvbuff);
break; //打印目录信息
case 5:
close(fd);
flag = 0;
pthread_exit(NULL); //退出
case 6:
UserSignIn(fd);
break;
default:
break;
}
}
}
int UserSignIn(int fd) //用户登录
{
printf("line :%d", __LINE__);
char uname[N] = "";
char passwd[M] = "";
char inputname[N] = "";
char inputpasswd[M] = "";
char recvbuff[M] = "";
FILE *fp = fopen("/home/yyh/C/FILESERVER/key/key.txt", "r");
if (NULL == fp)
{
ERR("fp");
return -1;
}
if (recv(fd, recvbuff, sizeof(recvbuff), 0) < 0)
{
ERR("recv");
return -1;
}
char *p = recvbuff;
int i = 0;
while (*p != ' ')
{
inputname[i] = *p;
i++;
p += 1;
}
strcpy(inputpasswd, p + 1);
while (1)
{
if (EOF == fscanf(fp, "%s %s", uname, passwd))
{
if (0 == errno)
{
if (send(fd, "账户或者密码错误\n", sizeof("账户或者密码错误\n"), 0) < 0)
{
ERR("send");
return -1;
}
bzero(recvbuff, sizeof(recvbuff));
continue;
}
else
{
ERR("fscanf");
return -1;
}
}
if (!strcmp(inputname, uname))
{
if (!strcmp(inputpasswd, passwd))
{
if (send(fd, "登录成功", sizeof("登录成功"), 0) < 0)
{
ERR("send");
return -1;
}
break;
}
else
{
if (send(fd, "账户或者密码错误", sizeof("账户或者密码错误"), 0) < 0)
{
ERR("send");
return -1;
}
bzero(recvbuff, sizeof(recvbuff));
continue;
}
}
}
if (fclose(fp) < 0)
{
ERR("fclose");
return -1;
}
}
int Register(int fd) //用户注册
{
char permit[128] = "";
char name[N] = ""; //储存要注册的姓名
char passwd[M] = ""; //储存要注册的密码
char recvbuff[128] = ""; //储存接受到的信息
ssize_t res = recv(fd, permit, sizeof(permit), 0);
if (res < 0)
{
ERR("recv");
return -1;
}
else if (0 == res)
{
if (send(fd, "传输中断,请检查网络后重试\n", sizeof("传输中断,请检查网络后重试\n"), 0) < 0)
{
ERR("send");
return -1;
}
return -1;
}
if (!strcmp(LICENCE, permit))
{
if (send(fd, "se\n", sizeof("se\n"), 0) < 0)
{
ERR("send");
return -1;
}
}
else
{
if (send(fd, "err", sizeof("err"), 0) < 0)
{
ERR("send");
return -1;
}
return -1;
}
FILE *fp = fopen("/home/yyh/C/FILESERVER/key/key.txt", "a");
if (NULL == fp)
{
ERR("fopen");
return -1;
}
bzero(name, sizeof(name));
bzero(passwd, sizeof(passwd));
if (recv(fd, recvbuff, sizeof(recvbuff), 0) < 0)
{
ERR("recv");
return -1;
}
char *p = recvbuff;
int i = 0;
while (*p != ' ')
{
name[i] = *p;
i++;
p += 1;
}
strcpy(passwd, p + 1);
if (fprintf(fp, "%s %s\n", name, passwd) < 0)
{
ERR("fprintf");
return -1;
}
printf("%s %s\n", name, passwd);
printf("注册成功\n");
if (fclose(fp) < 0)
{
ERR("fclose");
return -1;
}
}
int File_Info(int fd, char *fox)
{
char sendbuff[300] = "";
char pathname[64] = "";
char *p = fox + 2;
int i = 0;
while (*p) //获得数据包中的文件名称
{
pathname[i] = *p;
p += 1;
i++;
}
// FILE *fp = fopen("./file)
DIR *dir = opendir(pathname);
if (dir < 0)
{
ERR("open");
return -1;
}
struct dirent *dire;
while (1)
{
dire = readdir(dir);
if (NULL == dire)
{
if (0 == errno)
{
printf("读取完毕\n");
break;
}
else
{
ERR("readdir");
return -1;
}
}
if (!strcmp(dire->d_name, ".") || !strcmp(dire->d_name, ".."))
{
continue;
}
int num = sprintf(sendbuff, "%s%c", dire->d_name, ' ');
printf("%s", sendbuff);
if (send(fd, sendbuff, strlen(sendbuff), 0) < 0)
{
ERR("send");
return -1;
}
}
if (closedir(dir) < 0)
{
ERR("closedir");
return -1;
}
}
int RecvFile(int fd, char *fox)
{
char filename[128] = "";
char buff[516] = "";
char *p = fox + 2;
int i = 0;
while (*p) //获得数据包中的文件名称
{
filename[i] = *p;
p += 1;
i++;
}
ssize_t res;
int fs = open("./1.c", O_RDWR | O_CREAT | O_TRUNC, 0777);
if (fs < 0)
{
ERR("open");
return -1;
}
while (1)
{
res = recv(fd, buff, sizeof(buff), 0);
if (res < 0)
{
ERR("recv");
return -1;
}
else if (0 == res)
{
printf("客户端异常\n");
return -1;
}
if (write(fs, buff, res) < 0)
{
ERR("write");
return -1;
}
if (res < 516)
{
printf("接收完毕\n");
break;
}
}
close(fd);
}
int SendFile(int fd, char *recvbuff)
{
char name[64] = ""; //储存名称
char *p = recvbuff + 2;
int i = 0;
char sendbuff[516] = "";
while (*p) //获得数据包中的文件名称
{
name[i] = *p;
p += 1;
i++;
}
ssize_t res;
int fs = open(name, O_RDONLY);
if (fs < 0)
{
printf("没有找到文件\n");
ERR("open");
return -1;
}
while (1)
{
bzero(sendbuff, sizeof(sendbuff));
res = read(fs, sendbuff, sizeof(sendbuff));
if (res < 0)
{
ERR("read");
return -1;
}
else if (0 == res)
{
printf("文件发送完毕\n");
break;
}
if (send(fd, sendbuff, res, 0) < 0)
{
ERR("send");
break;
}
}
close(fd);
}
main.c
#include "func.h"
int main(int argc,const char *argv[])
{
info rng;
int fd = init();
int fs; //用于通信
struct sockaddr_in edg; //存储接收方信息的结构体
socklen_t size = sizeof(edg);
while (1)
{
fs = accept(fd, (struct sockaddr *)&edg, &size); // accept会提前指定。 每接入一个就会开两个线程以及一个临时端口用于通信
if (fs < 0)
{
ERR("accept");
return -1;
}
printf("IP:[%s]Prot:[%d]连接成功\n",inet_ntoa(edg.sin_addr),ntohs(edg.sin_port));
rng.fs = fs;
pthread_t thread;
if (pthread_create(&thread, NULL, callback, (void *)&rng) != 0)
{
ERR("pthread_create");
return -1;
}
}
if (close(fd) < 0 || close(fs)<0)
{
ERR("close");
return -1;
}
}
Makefile
all:x
x:func.o
x:main.o
gcc func.o main.o -o x
func.o:func.c
main.o:main.c
gcc -c func.c -o func.o
gcc -c main.c -o main.o
clean:
rm func.o main.o x
客户端
func.h
#define IP "192.168.8.190"
#define PORT 8888
#define N 48 //上传文件路径数组的大小
#define M 516 //用于发送的数组大小
#define ERR(err) \
do \
{ \
fprintf(stderr, "errline:%d\n", __LINE__); \
perror(err); \
} while (0)
int LogIn(int fd);
int Init();
int UpLode(int fd);
int DownLode(int fd);
int ShowDir(int fd);
int Register(int fd);
int Exits(int fd);
func.c
#include "func.h"
int LogIn(int fd)
{
ssize_t res = 0;
char sendstr[128] = "";
char sendstr1[64] = "";
char sendstr2[64] = "";
char recvstr[128] = "";
char request[24] = "";
int comd = sprintf(request,"%c%c%c",0,6,0);
if(send(fd,request,comd,0)<0)
{
ERR("send");
return -1;
}
while (1)
{
// send
bzero(sendstr, sizeof(sendstr));
printf("name:");
scanf("%s", sendstr1);
while (getchar() != '\n');
printf("passwd:");
scanf("%s", sendstr2);
while (getchar() != '\n');
sprintf(sendstr, "%s %s", sendstr1, sendstr2);
printf("line:%d\n",__LINE__);
if (send(fd, sendstr, sizeof(sendstr), 0) < 0)
{
ERR("send");
return -1;
}
printf("line:%d\n",__LINE__);
// recv
bzero(recvstr, sizeof(recvstr));
res = recv(fd, recvstr, sizeof(recvstr), 0);
if (res < 0)
{
ERR("recv");
return -1;
}
else if (0 == res)
{
printf("服务器异常\n");
break;
}
if (!strcmp(recvstr, "登录成功"))
{
printf("登录成功\n");
break;
}
else
{
printf("登录失败\n");
exit(0);
}
}
}
//上传文件
int UpLode(int fd) //参数为套接字的文件描述符
{
ssize_t res = 0;
char request[M] = "";
char pathname[N] = "";
char sendbuff[M] = "";
char recvbuff[N] = "";
printf("请输入要上传的文件路径:");
fflush(stdout);
fgets(pathname, sizeof(pathname), stdin);
pathname[strlen(pathname) - 1] = 0;
int fp = open(pathname, O_RDONLY);
if (fp < 0)
{
ERR("open");
return -1;
}
//发送请求
int size = sprintf(request, "%c%c%s%c", 0, 2, pathname, 0); //
if (send(fd, request, size, 0) < 0)
{
ERR("send");
return -1;
}
while (1)
{
bzero(sendbuff, sizeof(sendbuff));
res = read(fp, sendbuff, 516);
if (res < 0)
{
ERR("read");
return -1;
}
else if (0 == res)
{
printf("上传完毕\n");
break;
}
if (send(fd, sendbuff, res, 0) < 0)
{
ERR("send");
return -1;
}
}
}
//注册
int Register(int fd)
{
char permit[24] = "";
char recvbuff[128] = "";
char name[N] = ""; //储存要注册的姓名
char passwd[N] = ""; //储存要注册的密码
char sendbuff[500] = "";
char request[24] = "";
int sizes = sprintf(request, "%c%c%c", 0, 3, 0);
if (send(fd, request, sizes, 0) < 0)
{
ERR("send");
return -1;
}
printf("请输入管理员给的许可证:");
fgets(permit, sizeof(permit), stdin);
permit[strlen(permit) - 1] = 0;
if (send(fd, permit, sizeof(permit), 0) < 0)
{
ERR("send");
return -1;
}
if (recv(fd, recvbuff, sizeof(recvbuff), 0) < 0)
{
ERR("recv");
return -1;
}
if (!strcmp(recvbuff, "err"))
{
printf("许可证错误,请联系管理员后重试\n");
return -1;
}
else if (!strcmp(recvbuff, "se"))
{
printf("验证通过\n");
}
printf("请输入用户名:");
fgets(name, sizeof(name), stdin);
name[strlen(name) - 1] = 0;
printf("请输入密码:");
fgets(passwd, sizeof(passwd), stdin);
passwd[strlen(passwd) - 1] = 0;
int size = sprintf(sendbuff, "%s %s", name, passwd);
printf("%s\n", sendbuff);
if (send(fd, sendbuff, sizeof(sendbuff), 0) < 0)
{
ERR("send");
return -1;
}
}
//下载文件
int DownLode(int fd) //参数为套接字文件描述符
{
//先发请求
ssize_t res;
char filename[N] = "";
char requset[M] = "";
char recvbuff[M] = "";
printf("请输入要下载的文件名或者路径:");
fflush(stdout);
fgets(filename, sizeof(filename), stdin);
filename[strlen(filename) - 1] = 0;
int size = sprintf(requset, "%c%c%s%c", 0, 1, filename, 0);
if (send(fd, requset, size, 0) < 0)
{
ERR("send");
return -1;
}
int fk = open("./1.png", O_RDWR | O_CREAT | O_TRUNC, 0777);
if (fk < 0)
{
ERR("open");
return -1;
}
//服务器发来一个数据包
while (1)
{
res = recv(fd, recvbuff, sizeof(recvbuff), 0);
if (res < 0)
{
ERR("res");
return -1;
}
else if (0 == res)
{
printf("传输异常\n");
return -1;
}
if (write(fk, recvbuff, res) < 0) // write这里全部都需要检查一遍,还有网络字节序和本机字节序转换
{
ERR("write");
return -1;
}
if (res < M)
{
printf("传输完毕\n");
break;
}
}
}
//查看目录
int ShowDir(int fd) //参数为套接字文件描述符
{
ssize_t res, fds = 0;
char filedir[N] = "";
char request[M] = "";
char recvbuff[N] = "";
printf("请输入要打开的目录:");
fflush(stdout);
fgets(filedir, sizeof(filedir), stdin);
filedir[strlen(filedir) - 1] = 0;
//发送请求
int size = sprintf(request, "%c%c%s%c", 0, 4, filedir, 0);
if (send(fd, request, sizeof(request), 0) < 0)
{
ERR("send");
return -1;
}
//接收发过来的目录文件
while (1)
{
res = recv(fd, recvbuff, sizeof(recvbuff), 0);
if (res < 0)
{
ERR("recv");
return -1;
}
else if (0 == res)
{
printf("服务器断开连接\n");
return -1;
}
printf("%s\n", recvbuff); //直接打印出来
if (res < N)
{
printf("接收完毕\n");
break;
}
}
}
//退出程序
int Exits(int fd)
{
char buff[10] = "";
bzero(buff, sizeof(buff));
*(short *)buff = htons(5);
if (send(fd, buff, sizeof(buff), 0) < 0)
{
ERR("send");
return -1;
}
}
main.c
#include "func.h"
int main(int argc, const char *argv[])
{
int flag = 0;
struct sockaddr_in mss;
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
ERR("socket");
return -1;
}
mss.sin_addr.s_addr = inet_addr(IP);
mss.sin_family = AF_INET;
mss.sin_port = ntohs(PORT);
if (connect(fd, (struct sockaddr *)&mss, sizeof(mss)) < 0)
{
ERR("connect");
return -1;
}
LogIn(fd);//连接成功先登录
while (1)
{
printf("**************************************************\n");
printf("*****1.下载 2.上传 3.注册 4.打印目录信息 5.退出*****\n");
printf("**************************************************\n");
scanf("%d", &flag);
while (getchar() != '\n')
;
switch (flag)
{
case 1:
DownLode(fd);
break;
case 2:
UpLode(fd);
break;
case 3:
Register(fd);
break; //注册
case 4:
ShowDir(fd);
break;
case 5:
Exits(fd); //退出
break;
default:
break;
}
}
close(fd);
return 0;
}
Makefile
all:x
x:func.o
x:main.o
gcc func.o main.o -o x
func.o:func.c
main.o:main.c
gcc -c func.c -o func.o
gcc -c main.c -o main.o
clean:
rm func.o main.o x