天天看點

網絡程式設計:多線程程式設計模型

/*************************************************************************
    > File Name: src/echo_tcp_server_th.c
    > Author: sicaolong
    > Mail: [email protected] 
    > Created Time: 2018年07月18日 星期三 20時22分42秒
 ************************************************************************/
#include<iostream>
#include<stdio.h>
#include<netdb.h>
#include<unistd.h>
#include<stdlib.h>
#include<memory.h>
#include<signal.h>
#include<time.h>
#include <arpa/inet.h>
#include"msg.h"
#include <sys/types.h>
#include <sys/wait.h>
#include<error.h>
#include<errno.h>
#include<pthread.h>
int sockfd;
void sig_handler(int signo)
{
	if(signo==SIGINT)
	{
		printf("server close\n");
		//也屬于步驟6  關閉socket;
		close(sockfd);
		exit(1);
	}
}
//
void do_service(int fd)
{
	//和用戶端進行讀寫操作 ---雙向通信
	char buff[512];
	while(1)
	{
		memset(buff,0,sizeof(buff));
		printf("start read and write....\n");
		size_t size;
		if((size=read_msg(fd,buff,sizeof(buff)))<0)
		{
			perror("protocal error");
			break;
		}
		else if(size==0)
			break;
		else
		{
			printf("%s\n",buff);
			if(write_msg(fd,buff,sizeof(buff))<0)
			{
				if(errno==EPIPE)
					break;
				perror("protocal error");
			}
		}
	}
}
//輸出用戶端的相關資訊;
void out_fd(void *arg)
{
	int fd=(int)arg;
	struct sockaddr_in addr;
	socklen_t len=sizeof(addr);
	//從fd中獲得連接配接到用戶端的相關資訊;
	if(getpeername(fd,(struct sockaddr *)&addr,&len)<0)
	{
		perror("getpeername error");
		return ;
	}
	char ip[16];
	memset(ip,0,sizeof(ip));
	int port=ntohs(addr.sin_port);
	inet_ntop(AF_INET,&addr.sin_addr.s_addr,ip,sizeof(ip));
	printf("%16s(%5d)closed !\n",ip,port);

}
//線程運作的函數;
void *th_fn(void *arg)
{
	int fd=(int)arg;//強制轉換
	do_service(fd);//完成服務端與用戶端進行通訊;
	out_fd(fd);//輸出用戶端的相關資訊;
	close(fd);
	return (void *)0;
}
int main(int argc, char *argv[] )
{
	if(argc<2)
	{

		printf("usage: %s #port\n",argv[0]);
		exit(1);
	}
	if(signal(SIGINT,sig_handler)==SIG_ERR)
	{
		perror("signal sigint  error");
		exit(1);
	}
	//步驟一:建立socket
	//建立在核心中;是一個結構體;
	//AF_INET:ipv4  
	//sock_stream :tcp 協定;
	sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd<0)
	{
		perror("socket error");
		exit(1);
	}
//步驟二:将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;//"192.168.0.1";//監聽所有的網卡上面的用戶端的連結請求;
	if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0)
	{
		perror("bind error");
		exit(1);
	}
//步驟三  
	//調用listen 函數啟動監聽  制定port監聽;
	//通知系統去接受來自用戶端的連結請求;
	//及其昂接受到的用戶端請求放置到對應的隊列中
	//第二個參數: 指定隊列的長度;
	if(listen(sockfd,10)<0)
	{
		perror("listen error");
		exit(1);
	}
//步驟四:
	//調用 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)
	{
		int fd=accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);
		if(fd<0)
		{
			perror("accept error");
			exit(1);
		}
//步驟五:啟動子線程程去調用I/O函數(write/read)
	//和連結的用戶端進行雙向的通信;
		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;
}