1. 程式人生 > >網路程式設計三---多執行緒/程序解決併發問題

網路程式設計三---多執行緒/程序解決併發問題

#include <netinet/in.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <netdb.h>



void* doRead(void* arg)
{
	long connfd = (long)arg;
	char recvBuf[101] = "";
	int n = 0; 
    while((n = recv(connfd,recvBuf, sizeof(recvBuf),0 )) > 0)
    {
        printf("number of receive bytes = %d.\n", n);
		//傳送資料
        send(connfd, recvBuf, n, 0);
		char* bufTmp = recvBuf;
        while(*bufTmp != '\r' && *bufTmp !='\n')
			bufTmp++;
		*bufTmp = '\0';
        if(strcmp(recvBuf, "quit") == 0)
        {
            break;
        }
    }
	close(connfd);
 
	return NULL;
}
int main()
{
	struct sockaddr_in sockaddr;
	pthread_t thread_id;
	int one =1;
	int ret = 0;
	bzero(&sockaddr, sizeof(sockaddr));
	sockaddr.sin_family = AF_INET;
	sockaddr.sin_port = htons(8080);
	sockaddr.sin_addr.s_addr  = htonl(INADDR_ANY);
	long clientfd;
	int listenfd = socket(AF_INET, SOCK_STREAM, 0); //建立一個socket
	//將該套接字的繫結埠設為可重用
	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void*) &one,(socklen_t)sizeof(one));
	if(bind(listenfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
	{
		perror("bind error");
		return -1;
	}

	ret = listen(listenfd, 16);
	if(ret < 0)
	{
		perror("listen failed.\n");
		return -1;
	}
	
    struct sockaddr_in clientAdd;
    socklen_t len = sizeof(clientAdd);
	while(1)
	{
		clientfd = accept(listenfd, (struct sockaddr *)&clientAdd, &len);
		if(clientfd < 0)
		{
			perror("accept this time");
			continue;
		}
		if(clientfd > 0)
		{
			//由於同一個程序內的所有執行緒共享記憶體和變數,因此在傳遞引數時需作特殊處理,值傳遞
			pthread_create(&thread_id, NULL, doRead, (void *)clientfd);
			printf("thread %d created.\n",thread_id ); 
			pthread_detach(thread_id);
		}
	}
	close(listenfd);
	return 0;	
}
程式每當客戶端發起一個連線時,服務端接受客戶端連線accpet返回成功後,都建立一個執行緒,並將返回的客戶端的連線描述符fd傳遞給執行緒處理函式,線上程的處理函式doRead處理客戶端資料的收發。需要注意的是pthread_create傳遞的第四個引數傳遞的是將long型別的資料強轉成void *型別的資料,為什麼是long型別不是int型呢?這個在沒個機器上可能都不一樣,在筆者的機器上,int是4個位元組,而void*型別是8個位元組,所以必須將socket描述符定義成8個位元組long型別,否則int型裝成long型別編譯器報錯,此外需要注意的是這裡傳遞的不是clientfd的地址,如果傳遞了clientfd的地址,那麼每當來一個連線的時候,變了clientfd都會被改寫,而執行緒是共享程序的記憶體空間的,也就是說吐過傳遞了clienfd的地址所有執行緒都會使用同一個clienfd,到時其他客戶端無法和服務端通訊。