并发服务器中的 TCP多线程服务器与客户端(华清远见学员)

首先了解TCP服务端与客户端设计流程

        tcp的服务端的设计流程:

                       1.建立套接字socket        

                        2.绑定ip地址与端口号

                        3.建立监听队列

                        4.接受连接,产生新的套接字

                        5.数据的发送和接受

具体代码如下

#include <stdio.h>
#include <sys/types.h>	       /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc , char *argv[])
{
	int sockfd,ret,cockfd;
	char buf[128] = {0};
	ssize_t recv_bytes,secv_bytes;
	if(argc < 3)
	{
		fprintf(stderr,"usage: %s [IP] [PORT]\n",argv[0]);
		return -1;
	}
	//定义地址结构体大小
	struct sockaddr_in src,dest;
	socklen_t len = sizeof(src);
	socklen_t addrlen = sizeof(dest);
	//建立套接字
	sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1)
	{
		perror("socket fail");
		return -1;
	}
	//绑定
	src.sin_family = AF_INET;
	src.sin_port = htons(atoi(argv[2]));
	src.sin_addr.s_addr = inet_addr(argv[1]);
	//绑定套接字
	ret = bind(sockfd,(const struct sockaddr *)&src,len);
	if(ret == -1)
	{
		perror("bind");
		return -1;
	}
	//监听队列,监听连接请求,128为未决队列长度
	ret = listen(sockfd,128);
	if(ret == -1)
	{
		perror("listen");
		return -1;
	}
	//接受客户端连接请求,产生连接套接字,用于和客户端通信,通过dest保存客户端ip地址和port口号
	cockfd = accept(sockfd,(struct sockaddr *)&dest,&addrlen);
	if(cockfd == -1)
	{
		perror("accept");
		return -1;
	}
	//打印客户端ip和port,需要将网络字节序转换为主机字节序
	printf("client IP :%s PORT :%d\n",inet_ntoa(dest.sin_addr),ntohs(dest.sin_port));
	while(1)
	{
		memset(buf,0,sizeof(buf));
		//接收客户端的信息储存在buf中
		//recv_bytes = recv(cockfd,buf,sizeof(buf),0);
		//recv_bytes = read(cockfd,buf,sizeof(buf));
		recv_bytes = recvfrom(cockfd,buf,sizeof(buf),0,(struct sockaddr *)&dest,&addrlen);
		if(recv_bytes == -1)
		{
			perror("recv fail");
			return -1;
		}else if(recv_bytes == 0)//客户端退出返回0,服务器这里也退出
		{
			printf("client shutdown\n");
			break;
		}
		//secv_bytes = send(cockfd,buf,sizeof(buf),0);
		//secv_bytes = write(cockfd,buf,sizeof(buf));
		secv_bytes = sendto(cockfd,buf,sizeof(buf),0,(struct sockaddr *)&dest,addrlen);
		if(secv_bytes == -1)
		{
			perror("secv fail");
			return -1;
		}

		
		printf("buf : %s\n",buf);

	}
}

                        tcp客户端的设计流程: 

                                        1.建立套接字

                                        2.连接服务器 

                                        3.数据读写

具体代码如下:

        服务器与客户端的设计流程相差无几,客户端仅仅是多了个连接服务器,少了监听套接字和接受客户端两个步骤。

        连接客户端:

	ret = connect(sockfd,(const struct sockaddr *)&dest,len);
	if(ret == -1)
	{
		perror("connect fail");
		return -1;
	}

而多线程的TCP服务器可以连接多个客户端请求

        此时我们需要在主函数定义一个线程:

        只需在TCP服务器代码上稍微改动即可

pthread_t tid;

        之后,需要在连接客户端的代码前加一个while循环作为父进程,在父进程中定义pthread_detach()来回收子进程。(防止子进程过多占有资源) 

                代码如下:        

while(1)
	{
		cockfd = accept(sockfd,(struct sockaddr *)&dest,&addrlen);
		if(cockfd == -1)
		{
			perror("accept");
			return -1;
		}
	//打印客户端ip和port,需要将网络字节序转换为主机字节序
		printf("client IP :%s PORT :%d\n",inet_ntoa(dest.sin_addr),ntohs(dest.sin_port));
		num = pthread_create(&tid,NULL,thread_exec,&cockfd);
		if(num != 0)
		{
			errno = num;
			perror("pthread_create fail");
			return -1;
		}
		pthread_detach(tid);
	}

最后把数据的读写放在子线程中,遍可实现多个客户端的请求。

                代码如下:

void *thread_exec(void *arg)
{
	int cockfd = *(int *)arg;
	char buf[128] = {0};
	ssize_t recv_bytes,secv_bytes;
	//struct sockaddr_in dest;
	//socklen_t addrlen = sizeof(dest);
	while(1)
	{
		memset(buf,0,sizeof(buf));
		//接收客户端的信息储存在buf中
		recv_bytes = recv(cockfd,buf,sizeof(buf),0);
		//recv_bytes = read(cockfd,buf,sizeof(buf));
		//recv_bytes = recvfrom(cockfd,buf,sizeof(buf),0,(struct sockaddr *)&dest,&addrlen);
		if(recv_bytes == -1)
		{
			perror("recv fail");
			break;
		}else if(recv_bytes == 0)//客户端退出返回0,服务器这里也退出
		{
			printf("client shutdown\n");
			break;
		}
		secv_bytes = send(cockfd,buf,sizeof(buf),0);
		//secv_bytes = write(cockfd,buf,sizeof(buf));
	//	secv_bytes = sendto(cockfd,buf,sizeof(buf),0,(struct sockaddr *)&dest,addrlen);
		if(secv_bytes == -1)
		{
			perror("secv fail");
			break;
		}

		
		printf("buf : %s\n",buf);

	}
	pthread_exit(NULL);

}

此时注意我们的函数类型为 void *型,在对判断错误的过程中,不能使用return -1来进行返回,运行后会报警告。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值