1. 程式人生 > >【網路】TCP伺服器的實現

【網路】TCP伺服器的實現

socket程式設計基本概念

在TCP/IP協議中,IP地址+埠號標識個唯一的一個程序,“IP地址+埠號”就是socket

在TCP協議中,建立連線需要兩個程序各自有一個socket識別符號,這兩個socket組成的socket pair就標識著唯一的連線

相關概念介紹

網路位元組序

首先呢,我們都知道記憶體中的多位元組資料相對於記憶體地址有大端和小端之分,磁碟檔案中的多位元組資料相對於檔案的偏移也有大端和小端之分。

同理,網路資料流其實也有大端和小端之分。


網路資料流的地址這樣規定:先發出的資料是低地址,後發出的資料是高地址。

TCP/IP協議規定:網路資料流應採用大端位元組序,低地址高位元組

socket地址資料型別


用到的相關函式

socket

作用

建立socket套接字

標頭檔案

#include<sys/types.h>

#include<sys/socket.h>

函式原型

int socket(int domain,int type, int protocol);

引數

domain我們一般選擇AF_INET,表示IPv4

type表示傳輸資料的型別,有位元組流型別和SOCK_STREAM資料報型別SOCK_DGRAM

protocol表示建立的型別方式,我們此時設定為0

返回值

成功返回對應的檔案描述符

錯誤返回-1

bind

作用

進行伺服器的IP地址和埠號的繫結

標頭檔案

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet.h>

函式原型

int bind(int sockfd,const struct sockaddr *addr, socklen_t addrlen);

引數

sockfd是伺服器的套接字,就是socket函式的返回值

addr是socket伺服器的地址內容


addrlen是傳入的協議地址的大小

返回值

成功返回0

錯誤返回-1

listen

作用

設定套接字為監聽的狀態

標頭檔案

#include<sys/types.h>

#include<sys/socket.h>

函式原型

int listen(int sockfd,int backlog);

引數

sockfd是要設定的套接字

backlog是伺服器的最大等待佇列的個數

返回值

成功返回0

錯誤返回-1

connect

作用

使客戶端連線到伺服器

標頭檔案

#include<sys/types.h>

#include<sys/socket.h>

函式原型

int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);

引數

sockfd表示需要連線到伺服器的客戶端套接字

addr表示伺服器的地址和埠號

addrlen表示的是addr的大小

返回值

成功返回0

錯誤返回-1

accpet

作用

伺服器用來接受連線

標頭檔案

#include<sys/types.h>

#include<sys/socket.h>

函式原型

int accept(int sockfd, struct sockaddr * addr,  socklen_t *addrlen);

引數

sockfd表示需要連線的客戶端的套接字

addr是傳出引數,用來傳出客戶端的IP地址和埠號

addrlen標示傳出引數的長度

返回值

成功返回0

錯誤返回-1

TCP伺服器的程式碼實現

普通版本

server.c

#include<stdio.h>
#include<sys/socket.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<error.h>

#define SERV_PORT 9999
#define _BACKLOG_ 10

void Usage()
{
	printf("Usage: [ipaddr]\n");
}

int main(int argc,char* argv[])
{
	if(argc != 2)
	{
		Usage();
		return -1;
	}

	//1、呼叫socket來開啟一個網路通訊埠
	int sock = socket(AF_INET,SOCK_STREAM,0);
	if(sock < 0)
	{
		perror("socket");
		exit(1);
	}

	//2、伺服器來繫結一個固定的IP地址和埠號
	struct sockaddr_in serverSocket;//定義服務端的套接字
	struct sockaddr_in clientSocket;//定義客戶端的套接字
	
	bzero(&serverSocket,sizeof(serverSocket));//歸零初始化

	serverSocket.sin_family = AF_INET;//設定地址的型別
	serverSocket.sin_addr.s_addr = inet_addr(argv[1]);

	serverSocket.sin_port = htons(SERV_PORT);//設定埠號,SERV_PORT為自定義的巨集,值設定為9999

	if(bind(sock,(struct sockaddr*)&serverSocket,sizeof(struct sockaddr_in))< 0)//進行繫結,並進行判斷
	{
		perror("bind");
		close(sock);
		exit(2);
	}

	//3、進行監聽
	if(listen(sock,_BACKLOG_)< 0)
	{
		perror("listen");
		close(sock);
		exit(3);
	}

	printf("繫結和監聽成功...請等待連線...\n");

	while(1)
	{
		//4、接受連線
		socklen_t len = 0;//定義長度len
		int clientSock = accept(sock, (struct sockaddr*)&clientSocket,&len);//接受連線
		if(clientSock < 0)
		{
			perror("accpet");
			exit(4);
		}

		char buf[INET_ADDRSTRLEN];//定義緩衝區,用來存放IP字串
		memset(buf,0,sizeof(buf));//初始化為0
	
		while(1)
		{
			char tmpBuf[1024];//定義空間來儲存收到與傳送的訊息
			ssize_t ret = read(clientSock,tmpBuf,sizeof(tmpBuf));//從客戶端套接字中讀入資料
			if(ret < 0)
			{
				perror("read");
				exit(5);
			}

			tmpBuf[ret] = '\0';

			printf("#client :# %s\n",tmpBuf);//列印從客戶端讀入的資料
			printf("#server :# ");
			memset(tmpBuf,'\0',sizeof(tmpBuf));//將buf空間置零
			fgets(tmpBuf,sizeof(tmpBuf),stdin);//讀入需要傳送的資料
			write(clientSock,tmpBuf,strlen(tmpBuf));
			printf("請等待...\n");
		}
	}

	close(sock);
	return 0;
}

client.c

#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<unistd.h>
#include<sys/types.h>
#include<netinet/in.h>

#define SERVER_PORT 9999

void Usage()
{
	printf("Usage: [client_ip] [client_port]\n");
}

int main(int argc, char* argv[])
{
	if(argc != 3)
	{
		Usage();
		return 1;
	}

	//呼叫socket來開啟一個網路埠
	char buf[1024];
	char* client_ip = argv[1];
	memset(buf,'\0',sizeof(buf));
	int sock = socket(AF_INET,SOCK_STREAM,0);

	//定義sockaddr_in結構體,並進行初始化
	struct sockaddr_in serverSocket;
	serverSocket.sin_family = AF_INET;
	serverSocket.sin_addr.s_addr = inet_addr(argv[1]);
	serverSocket.sin_port = htons(SERVER_PORT);

	//進行伺服器的連結
	int ret = connect(sock,(struct sockaddr*)&serverSocket,sizeof(serverSocket));
	if(ret < 0)
	{
		printf("連線失敗...\n");
		return 2;
	}
	
	printf("連線成功...\n");

	while(1)
	{
		printf("#client :# ");
		fflush(stdin);
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf)-1] = '\0';

		if(strcmp(buf,"quit")==0)
			break;

		write(sock,buf,sizeof(buf));
	
		printf("請等待伺服器響應...\n");
		read(sock,buf,sizeof(buf));
		printf("#server :# %s",buf);
	}

	close(sock);
	return 0;
}

多程序版本

server.c

#include<stdio.h>
#include<sys/types.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>

void Usage()
{
	printf("Usage: [ipaddr] [port]\n");
	exit(1);
}

int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		Usage();
	}

	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0)
	{
		perror("sock");
		exit(2);
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	addr.sin_addr.s_addr = inet_addr(argv[1]);

	
	if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("bind");
		exit(3);
	}

	if(listen(sockfd,10) < 0) 
	{
		perror("listen");
		exit(4);
	}

	printf("wait connect...\n");

	while(1)
	{
		struct sockaddr_in saddr;
	        socklen_t straddr = sizeof(saddr);
		int fd = accept(sockfd,(struct sockaddr *)&saddr,&straddr);

		printf("accept connect...\n");

		if(fd < 0)
		{
			continue;
		}

		pid_t pid = fork();
	
		if(pid < 0)
		{
			perror("fork");
			exit(6);
		}
		else if(pid > 0)
		{
			printf("find a new client... ip : %s ... port : %d\n",inet_ntoa(saddr.sin_addr),ntohs(saddr.sin_port));
			char buf[1024];
			while(1)
			{
				int s = read(fd, buf,sizeof(buf)-1);
				if(s < 0)
				{
					perror("read");
					exit(7);
				}
				else if(s == 0)
				{
					printf("connect quit... ip : %s ... port : %d\n",inet_ntoa(saddr.sin_addr),ntohs(saddr.sin_port));
					close(fd);
					break;
				}
				else
				{
					buf[s] = '\0';
					printf("#client: %s\n",buf);
					write(fd,buf,strlen(buf));
				}
			}
			close(fd);
			wait(pid,NULL,0);
		}
		else
		{
			continue;
		}
	}
	close(sockfd);
	return 0;
}

client.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<string.h>
#include<sys/socket.h>

void Usage()
{
	printf("Usage: [ipaddr] [port]\n");
	exit(1);
}

int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		Usage();
	}

	//開啟一個網路埠
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	
	if(sockfd < 0)
	{
		perror("socket");
		exit(2);
	}

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	addr.sin_addr.s_addr = inet_addr(argv[1]);

	if(connect(sockfd,(struct sockaddr*)&addr, sizeof(addr))< 0)
	{
		perror("connect");
		exit(3);
	}

	printf("連線成功...\n");
	char buf[1024];

	while(1)
	{
		printf("#client: ");
		fflush(stdout);
		int s = read(0,buf,sizeof(buf)-1);
		if(s <= 0)
		{
			perror("read");
			exit(4);
		}
		else
		{
			buf[s-1] = 0;
			write(sockfd,buf,strlen(buf));
		}

		int ret = read(sockfd,buf,sizeof(buf)-1);
		if(ret < 0)
		{
			perror("read");
			exit(5);
		}
		else if(ret == 0)
		{
			printf("連線已斷開...\n");
			break;
		}
		else
		{
			buf[s] = '\0';
			printf("#server: %s\n",buf);
		}
	}
	close(sockfd);

	return 0;
} 

多執行緒版本

server.c

#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<unistd.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<pthread.h>
#include<string.h>

void Usage()
{
	printf("Usage: [addrip] [port]\n");
	exit(1);
}

int StartUp(char* argv[])
{
	//建立介面
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd < 0)
	{
		perror("socket");
		exit(2);
	}

	//進行繫結
	
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	addr.sin_addr.s_addr = inet_addr(argv[1]);

	if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr)) < 0)
	{
		perror("bind");
		exit(3);
	}
	
	//進行監聽
	if(listen(sockfd,10) < 0)
	{
		perror("listen");
		exit(4);
	}

	return sockfd;
}

void* Myhandler(void* arg)
{
	int fd = (int)arg;
	char buf[1024];

	while(1)
	{
		ssize_t s = read(fd,buf,sizeof(buf));
		if(s < 0)//error
		{
			perror("read");
			exit(6);
		}
		else if(s == 0)//quit
		{
			printf("client quit...\n");
			close(fd);
			break;
		}
		else//write
		{
			buf[s] = 0;
			printf("#client : %s\n",buf);
			write(fd,buf,strlen(buf));
		}
	}

}

int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		Usage();
	}

	int sockfd = StartUp(argv);	

	printf("初始化成功...等待連線...\n");

	while(1)
	{
		struct sockaddr_in addr;
		socklen_t addrlen = sizeof(addr);
		int fd = accept(sockfd,(struct sockaddr*)&addr,&addrlen);
		if(fd < 0)
		{
			continue;
		}
		printf("連線成功... ip : %s , port: %d\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
		
		pthread_t td;
		pthread_create(&td,NULL,Myhandler,(void*)fd);
		pthread_detach(td);
	}

	close(sockfd);
	return 0;
}

client.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<error.h>
#include<pthread.h>
#include<string.h>
static void Usage()
{
	printf("Usage : [ipaddr] [port]\n");
	exit(1);
}

int StartUp(char* argv[])
{
	//開啟介面
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	addr.sin_addr.s_addr = inet_addr(argv[1]);

	if(connect(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("connect");
		exit(2);
	}
	return sockfd;
}

int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		Usage();
	}

	int sockfd = StartUp(argv);

	printf("連線成功...\n");

	while(1)
	{
		printf("#client : ");
		fflush(stdout);
		char buf[1024];
		ssize_t s = read(0,buf,sizeof(buf)-1);
		if(s <= 0)
		{
			perror("read");
			exit(2);
		}
		else
		{
			buf[s-1] = '\0';
			write(sockfd,buf,strlen(buf));
		}

		s = read(sockfd,buf,sizeof(buf)-1);
		if(s == 0)
		{
			printf("server quit...\n");
			break;
		}
		else if(s < 0)
		{
			perror("read");
			exit(3);
		}
		else
		{
			buf[s] = '\0';
			printf("#server : %s\n",buf);
		}
	}
	close(sockfd);
	return 0;
}