运行基本ok!(第三版修改结果)
服务器server端:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <bits/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <time.h>
//后续优化目标:
//更完善的socket多了链表、事件、SSL、结构体封装以及宏定义、更完整的判断返回/处理错误语句。
void usage()
{
printf("usage:\n");
printf("-p: server listen port\n");
printf("eg: ./tcp_server -p 8080\n");
}
int main(int argc, char *argv[])
{
int port = 0;
int ch = 0;
while(1)
{
static struct option opts[]=
{
{"port", 1, NULL, 'p'},
{0, 0, 0, 0}
};
ch = getopt_long(argc, argv, "p:", opts, NULL);
if(ch == -1){
break;
}
switch (ch)
{
case 'p':
port = atoi(optarg);
break;
default:
usage();
return -1;
}
}
if(!port){
usage();
return -1;
}
printf("port:%d\n", port);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket is error!\n");
return -1;
}
printf("socket ok!\n");
struct sockaddr_in server_addr, client_addr, peer_addr;
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = INADDR_ANY;
int on = 1;
if((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) <0 ) //设置端口可重用,能重复绑定套接字
{
perror("setsockopt failed!\n");
return -2;
}
if(bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) != 0)
{
perror("bind is error!\n");
close(sockfd);
return -2;
}
printf("bind ok!\n");
if(listen(sockfd, SOMAXCONN) != 0)
{
perror("listen is error!\n");
close(sockfd);
return -3;
}
printf("listen ok!\n");
fd_set fds, readFds;
FD_ZERO(&fds);
FD_SET(sockfd, &fds);
while(1)
{
readFds = fds;
int rc = select(FD_SETSIZE, &readFds, NULL, NULL, NULL);
if(rc == -1)
{
perror("select is error!\n");
break;
}
printf("select ok!\n");
socklen_t len;
//无法支持两个以上的client连接
int clientsock[2], numsock = 0, maxsock = 2;
for(int i = 0; i < FD_SETSIZE; i++)
{
if(FD_ISSET(i, &readFds))
{
if(i == sockfd)
{
if(numsock < maxsock)
{
//最好用双向链表,server指向client创建的结构体
clientsock[numsock] = accept(sockfd, (struct sockaddr*)&client_addr, &len);
if(clientsock[numsock] == -1)
{
perror("accept is error!\n");
}
time_t t;
time(&t);
printf("accept client %d time is: %s\n", clientsock[numsock], ctime(&t));
FD_SET(clientsock[numsock], &fds);
numsock++;
}
else{
printf("Ran out of socket space!\n");
}
}
else{
char message[1024] = {0};
int in;
int peerlen = sizeof(peer_addr);
getpeername(i, (struct sockaddr*)&peer_addr, &peerlen);
char *peer_ip, *filepath;
inet_ntop(AF_INET, &peer_addr.sin_addr, peer_ip, sizeof(peer_ip));
int peer_port = ntohs(peer_addr.sin_port);
sprintf(filepath, "/tmp/server/%s-%d.log", peer_ip, peer_port);
int fd = open(filepath, O_WRONLY | O_CREAT | O_APPEND);
//接收完一个client的数据才能接收下一个,无法同时接收多个,并且存在卡死的情况
if((in = recv(i, &message[0], sizeof(message), 0)) > 0)
{
if(write(fd, message, in) < 0){
FD_CLR(i, &fds);
close(i);
break;
}
numsock--;
}
FD_CLR(i, &fds);
close(i);
}
}
}
}
close(sockfd);
return 0;
}
客户client端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <sys/select.h>
void usage()
{
printf("usage:\n");
printf("-i: server IP address\n");
printf("-p: server port\n");
printf("-f: the file to be sent\n");
printf("-s: the size of the data sent\n");
printf("-t: send time interval\n");
printf("eg: ./tcp_client -i 127.0.0.1 -p 8080 -f /tmp/test.txt -s 100 -t 50\n");
}
int main(int argc, char *argv[])
{
char *ip = NULL;
char *filename = NULL;
int port = 0, filesize = 0, time = 0;
int ch = 0;
while(1)
{
static struct option opts[]=
{
{"ipaddr", 1, NULL, 'i'},
{"port", 1, NULL, 'p'},
{"file", 1, NULL, 'f'},
{"filesize", 1, NULL, 's'},
{"time", 1, NULL, 't'},
{0, 0, 0, 0}
};
ch = getopt_long(argc, argv, "i:p:f:s:t:", opts, NULL);
if(ch == -1){
break;
}
switch (ch){
case 'i':
ip = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 'f':
filename = optarg;
break;
case 's':
filesize = atoi(optarg);
break;
case 't':
time = atoi(optarg);
break;
default:
usage();
return -1;
}
}
if(!ip || !port || !filename || !filesize)
{
usage();
return -1;
}
printf("ip:%s, port:%d, filename:%s\n", ip, port, filename);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket is error!\n");
return -2;
}
printf("socket ok!\n");
struct sockaddr_in serveraddr;
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family= AF_INET;
inet_pton(AF_INET, ip, &serveraddr.sin_addr.s_addr);
serveraddr.sin_port= htons(port);
if(connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) != 0)
{
perror("connect is error!\n");
close(sockfd);
return -3;
}
printf("connect ok!\n");
//使用timer_create定时器,编译时在命令最后加上-lrt
timer_t timerid;
struct sigevent evp;
memset(&evp, 0, sizeof(struct sigevent));
evp.sigev_notify = SIGEV_SIGNAL;
evp.sigev_signo = SIGUSR1;
if(timer_create(CLOCK_REALTIME, &evp, &timerid) == -1)
{
perror("timer_create is fail!\n");
close(sockfd);
return -4;
}
struct itimerspec its;
its.it_value.tv_sec = 1;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = time/1000;
its.it_interval.tv_nsec = 0;
if(timer_settime(timerid, 0, &its, NULL) == -1)
{
perror("timer_set is fail!\n");
close(sockfd);
return -4;
}
printf("timer_create is successful!\n");
fd_set fsets, writeFsets;
FD_ZERO(&fsets);
FD_SET(sockfd, &fsets);
// O_CLOEXEC 自动关闭文件,读文件不需要自动创建
int fd = open(filename, O_RDONLY | O_CLOEXEC);
while(1)
{
writeFsets = fsets;
int rs = select(FD_SETSIZE, NULL, &writeFsets, NULL, NULL);
if(rs == -1){
perror("select is error!\n");
break;
}
if(rs == 0){
printf("timeout!\n");
break;
}
printf("select is ok!\n");
for(int i = 0; i < FD_SETSIZE; i++)
{
if(FD_ISSET(i, &writeFsets))
{
if(i == sockfd)
{
FD_SET(i, &fsets);
}
else
{
//存在一直读取开头filesize大小的文件数据的情况,并且容易读取过小或过大
char buf[filesize];
int num = read(fd, buf, sizeof(buf));
if(num > 0){
if(send(i, buf, sizeof(buf), 0) > 0){
printf("send is ok!\n");
}
}
else{
FD_CLR(i, &fsets);
close(i);
return -5;
}
}
}
}
}
close(sockfd);
return 0;
}
版权声明:本文为weixin_56937723原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。