首页 > 代码库 > 服务器如何给多个客户端分配端口号

服务器如何给多个客户端分配端口号

在一台计算机上,使用socket通信时,不同进程区分网络通信的连接依靠三个参数:通信所用协议、地址IP、端口号。

对于服务器端来说,通过bind、listen,之后accept建立新的连接,accept返回新连接的句柄,这样就建立了一条连接。那么新建立的连接使用的端口号是否和listen所用端口号相同呢?以前我一直以为服务器会随机分配一个新的端口号来使用,后来发现错了。

因为1、现在使用多路IO复用epoll等,配置好点的服务器可以支持数十万个并发连接,端口号为16位,最多才2^16-1,且加上一些常用的端口号不能使用,可用的端口号都没那么多。2、现在服务器大多使用防火墙,防火墙只对特定端口开放。如果accept随机分配端口号,会不能通过防火墙。

TCP/IP协议中,IP协议是端到端的协议,它只是负责把把数据发送到端,交付给上层而已。运输层TCP、UDP加上了端口号,目的是区分不同的应用。其中TCP还实现了流量控制、可靠传输等,而UDP只是应该是没有对IP层数据进行处理了。

在以往的知识中,我知道一个应用程序只能使用一个端口号,如果accept返回的句柄还是使用listen的端口号,那么怎么实现通信呢?如果建立多个连接,应用程序怎么区收到的信息来自哪个客户端呢?

现在才理解到accept返回的句柄建立的连接包括四部分:源IP、源端口号、目的IP、目的端口号。这样在一个应用程序中,就算和多个客户端建立连接,在收到数据后,应用程序通过目的IP和目的端口号也能区分是哪一条连接。

通过一个echo服务器来验证一下,client和server都在同一台机器上:

服务器监听8000端口,在未建立连接时,可以看到在监听8000


在通过一个客户端建立连接后,可以看到建立了一条连接,服务器端的端口号是8000,监听的还是8000。


在连接一个客户端,可以看到建立了两条连接,服务器端都是使用8000,监听的还是8000。


下面是server端代码:

#include<stdio.h>
#include<sys/socket.h>
#include<unistd.h>
#include<errno.h>
#include<netinet/in.h>
void str_echo(int sockfd)
{
	int n;
	char buf[1024];
again:
	while((n=read(sockfd,buf,1024))>0)
	{
		write(sockfd,buf,n);
	}
	if(n<0&&errno==EINTR)
		goto again;
	else if(n<0)
		printf("str_error:read error");
}
int main(int argc, char **argv)
{
	int listenfd, connfd;
	pid_t childid;
	socklen_t clilen;
	struct sockaddr_in cliaddr, servaddr;
	
	listenfd=socket(AF_INET, SOCK_STREAM, 0);
	
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//host IP
	servaddr.sin_port=htons(8000);//port
	
	bind(listenfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr));
	
	listen(listenfd,5);//最多处理五个监听
	
	for(;;)
	{
		clilen=sizeof(cliaddr);
		//进场阻塞在accept上
		connfd=accept(listenfd, (struct sockaddr*)&cliaddr,&clilen);
		
		if(childid=fork()==0)//child
		{
			close(listenfd);//在子进程中关闭监听
			str_echo(connfd);//处理监听的连接
			exit(0);
		}
	}
	close(listenfd);
}

客户端代码:

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
void str_cli(FILE *fp, int sockfd)
{
	char sendline[1024],recvline[1024];
	while(fgets(sendline, 1024, fp)!=NULL)
	{
		//发给Server
		write(sockfd,sendline,sizeof(sendline));
		
		//读Server
		if(read(sockfd,recvline,1024)==0)
		{
			printf("str_cli:server terminated prematurely\n");
			exit(0);
		}
		//fputs(recvline,stdout);
		printf("%s",recvline);
	}
}
int main(int argc, char **argv)
{
	int sockfd;
	struct sockaddr_in servaddr;
	
	if(argc!=2)
	{
		printf("usage:client IPaddress \n");
		exit(0);
	}
	sockfd=socket(AF_INET, SOCK_STREAM, 0);
	
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(8000);
	
	
	//把输入的IP存到sin_addr中
	inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
	printf("server IP:%s\n",argv[1]);
	//建立连接
	connect(sockfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr));
	
	str_cli(stdin,sockfd);
	
	exit(0);
}


服务器如何给多个客户端分配端口号