Linux网络编程示例之多线程

server.c

#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>

int sockfd;

void sig_handler(int signo)
{
	if (signo == SIGINT) {
		printf("server close\n");
		/*关闭socket*/
		close(sockfd);
		exit(1);
	}
}

void* th_fn(void* arg)
{
	int fd = (int)arg;

    /*和客户端进行读写操作(双向通信)*/
	char buff[512];
    while (1) {
        memset(buff,0,sizeof(buff));
        size_t size = 0;
        if ((size = read(fd,buff,sizeof(buff))) <= 0) {
            perror("server read error");
            continue;
        } else {
            printf("%s",buff);
            //将读取到的数据在发送回客户端
            if (write(fd,buff,sizeof(buff)) < 0) {
                perror("server write data to client error");
            }
        }
    }
	return (void*)0;
}

int main(int argc,char* argv[])
{
    if (argc<2) {
		printf("usage:%s #port\n",argv[0]);
		exit(0);
	}

	if (signal(SIGINT,sig_handler) == SIG_ERR) {
		perror("signal sigint error");
		exit(1);
	}

    /* 步骤1:创建socket套接字
	 * 注:socket创建在内核中,是一个结构体
	 * AF_INET:IPV4
	 * SOCK_STREAM:tcp协议
	 * */
	sockfd = socket(AF_INET,SOCK_STREAM,0);

    /*
     * 步骤2:调用bind函数将socket和地址(包括ip,port)进行绑定
     * */
	struct sockaddr_in serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
	//往地址中填入ip,port,internet地址族类型
	serveraddr.sin_family = AF_INET;//IPV4
	serveraddr.sin_port = htons(atoi(argv[1]));//port
	serveraddr.sin_addr.s_addr = INADDR_ANY;
	if (bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0) {
		perror("bind error");
		exit(1);
	}

    /*
	 * 步骤3:调用listen函数启动监听(指定port监听)
	 * 通知系统接受来自客户端的连接请求(将接受到的客户端连接请求放置到对应的队列中)
	 * 第二个参数:允许连接的客户端数量
	 * */
	if (listen(sockfd,10)<0) {
		perror("listen error");
		exit(1);
	}

    /*
	 * 步骤4:调用accept函数从队列中获得一个客户端的请求连接,并返回新的socket描述副
	 * 注意:若没有客户端连接,调用此函数后会阻塞直到获得一个客户端连接
	 * */
	struct sockaddr_in clientaddr;
	socklen_t clientaddr_len = sizeof(clientaddr);

    //设置线程的分离属性
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);

    while (1) {
        //主控线程负责调用accept去获得客户端的连接
		int fd = accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);
        if (fd<0) {
			perror("accept error");
			continue;
		}

        /*
		 * 步骤5:启动子线程调用IO函数(read/write)和连接的客户端进行双向通信
		 * */
		pthread_t th;
		int err;
		if ((err = pthread_create(&th,&attr,th_fn,(void*)fd)) != 0) {
			perror("pthread create error");
		}
		pthread_attr_destroy(&attr);
    }

    return 0;
}

client.c

#include <netdb.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <unistd.h>
#include <stdio.h>


int main(int argc,char* argv[])
{
    if (argc < 3) {
		printf("usage:%s ip port\n",argv[0]);
		exit(1);
	}

    /*步骤1:创建socket*/
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd<0)
	{
		perror("socket error");
		exit(1);
	}

    struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(atoi(argv[2]));
	//将ip地址转换成网络字节序后填入serveraddr中
	inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);
    /*步骤2:客户端调用connect函数连接服务器端*/
	if (connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr)) < 0) {
		perror("connect error");
		exit(1);
	}


    /*步骤3:调用IO函数(read/write)和服务器端进行双向通信*/
    char buff[1024];
	size_t size;
    while (1) {
        memset(buff,0,sizeof(buff));
		size = read(STDIN_FILENO,buff,sizeof(buff)); // 读取客户输入数据
        if (size < 0) {
			continue;
		}
        // buff[size-1] = '\0';
        //客户输入数据读取成功,将数据发往服务端
        if (write(sockfd,buff,sizeof(buff)) > 0) {
            memset(buff,0,sizeof(buff));
            if (read(sockfd,buff,sizeof(buff)) > 0) {
				write(STDOUT_FILENO,buff,sizeof(buff));

			}
        }
    }

    return 0;
}

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值