服務器如何給多個客戶端分配端口號
來源:程序員人生 發布時間:2014-12-15 09:13:13 閱讀次數:6170次
在1臺計算機上,使用socket通訊時,不同進程辨別網絡通訊的連接依托3個參數:通訊所用協議、地址IP、端口號。
對服務器端來講,通過bind、listen,以后accept建立新的連接,accept返回新連接的句柄,這樣就建立了1條連接。那末新建立的連接使用的端口號是不是和listen所用端口號相同呢?之前我1直以為服務器會隨機分配1個新的端口號來使用,后來發現錯了。
由于1、現在使用多路IO復用epoll等,配置好點的服務器可以支持數10萬個并發連接,端口號為16位,最多才2^16⑴,且加上1些經常使用的端口號不能使用,可用的端口號都沒那末多。2、現在服務器大多使用防火墻,防火墻只對特定端口開放。如果accept隨機分配端口號,會不能通過防火墻。
TCP/IP協議中,IP協議是端到真個協議,它只是負責把把數據發送到端,交付給上層而已。運輸層TCP、UDP加上了端口號,目的是辨別不同的利用。其中TCP還實現了流量控制、可靠傳輸等,而UDP只是應當是沒有對IP層數據進行處理了。
在以往的知識中,我知道1個利用程序只能使用1個端口號,如果accept返回的句柄還是使用listen的端口號,那末怎樣實現通訊呢?如果建立多個連接,利用程序怎樣區收到的信息來自哪一個客戶端呢?
現在才理解到accept返回的句柄建立的連接包括4部份:源IP、源端口號、目的IP、目的端口號。這樣在1個利用程序中,就算和多個客戶端建立連接,在收到數據后,利用程序通過目的IP和目的端口號也能辨別是哪1條連接。
通過1個echo服務器來驗證1下,client和server都在同1臺機器上:
服務器監聽8000端口,在未建立連接時,可以看到在監聽8000

在通過1個客戶端建立連接后,可以看到建立了1條連接,服務器真個端口號是8000,監聽的還是8000。

在連接1個客戶端,可以看到建立了兩條連接,服務器端都是使用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);//最多處理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
");
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
");
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
",argv[1]);
//建立連接
connect(sockfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr));
str_cli(stdin,sockfd);
exit(0);
}
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈