TCP的Socket通信编程

运行基本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版权协议,转载请附上原文出处链接和本声明。