您的位置:首页 > 理论基础 > 计算机网络

关于TCP编程,你是否为此迷惑过

2011-11-10 22:26 405 查看
TCP的服务端编程,一般API的调用顺序是socket, bind, listen, accept, send, recv等。TCP的客户端编程,一般API的调用顺序是socket, connect, send, recv。

在服务端,accept函数的其中一个入参是listen-socket,会返回一个新的connection-socket。通过connection-socket,调用getpeername,可以得到客户端的IP和端口。通过connection-socket,调用getsockname,可以得到本地的IP和端口。accept函数的其中一个出参,也可以返回客户端的IP和端口。

通常如果客户端创建socket后,调用connect前,没有调用bind来绑定本地的IP和端口,那么connect建立的连接的本端IP是API自动通过目的IP选择的,而本端端口是随机的。这没有问题。通过accept函数返回的connection-socket,调用getsockname返回的IP是服务器端listen的IP。这也没问题。问题是,通过accept函数返回的connection-socket,调用getsockname返回的端口是什么呢?

按我直观的、Intuitive的理解,这个端口应该是个随机的端口,因为connection-socket是新建的socket,是和listen-socket不同的socket。但事实令我不解,这个端口竟然和listen-socket绑定的端口是相同的,不知你是否也跟我一样想法过。上代码。

/* Sample TCP server */

/* www.cs.ucsb.edu/~almeroth/classes/W01.176B/hw2/examples/tcp-server.c */

#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <strings.h>

#include <stdio.h>

#include <stdlib.h>
#include <unistd.h>

int main(int argc, char**argv)

{

int listenfd,connfd,n;

struct sockaddr_in servaddr,cliaddr,localaddr;

socklen_t clilen;

socklen_t locallen;

pid_t     childpid;

char mesg[1000];

listenfd=socket(AF_INET,SOCK_STREAM,0);

bzero(&servaddr,sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

servaddr.sin_port=htons(32000);

bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));

listen(listenfd,1024);

for(;;)

{

clilen=sizeof(cliaddr);

connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&clilen);

locallen=sizeof(localaddr);

getsockname(connfd,(struct sockaddr*)&localaddr,&locallen);

printf("local addr=%X\n",htonl(*((unsigned int*)&localaddr.sin_addr)));

printf("local port=%d\n",htons(localaddr.sin_port));

if ((childpid = fork()) == 0)

{

close (listenfd);

for(;;)

{

n = recvfrom(connfd,mesg,1000,0,(struct sockaddr *)&cliaddr,&clilen);

sendto(connfd,mesg,n,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));

printf("-------------------------------------------------------\n");

mesg
= 0;

printf("Received the following:\n");

printf("%s",mesg);

printf("-------------------------------------------------------\n");

}

}

close(connfd);

}

}
/* Sample TCP client */

/* www.cs.ucsb.edu/~almeroth/classes/W01.176B/hw2/examples/tcp-client.c */

#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <strings.h>

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char**argv)

{

int sockfd,n;

struct sockaddr_in servaddr,cliaddr;

char sendline[1000];

char recvline[1000];

if (argc != 2)

{

printf("usage:  client <IP address>\n");

exit(1);

}

sockfd=socket(AF_INET,SOCK_STREAM,0);

bzero(&servaddr,sizeof(servaddr));

servaddr.sin_family = AF_INET;

servaddr.sin_addr.s_addr=inet_addr(argv[1]);

servaddr.sin_port=htons(32000);

connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

while (fgets(sendline, 10000,stdin) != NULL)

{

sendto(sockfd,sendline,strlen(sendline),0,

(struct sockaddr *)&servaddr,sizeof(servaddr));

n=recvfrom(sockfd,recvline,10000,0,NULL,NULL);

recvline
=0;

fputs(recvline,stdout);

}

}


这两个tcp-server和tcp-client程序是我从www.cs.ucsb.edu借鉴过来的,我只加了getsockname相关部分。服务端程序执行结果:

$ ./tcpserver
local addr=7F000001
local port=32000
-------------------------------------------------------
Received the following:
abcdef
-------------------------------------------------------
-------------------------------------------------------
Received the following:
ghijkl
-------------------------------------------------------
^C
客户端程序执行结果:

$ ./tcpclient 127.0.0.1
abcdef
abcdef
ghijkl
ghijkl
^C
看到了吧,accept函数返回的connection-socket,调用getsockname返回的端口竟然和listen-socket的端口是相同的,都是32000。这是为什么呢?为什么这样counter-intuitive呢?

问了Google,问了对TCP有研究的高人,他又问了高人,才明白。原来我们看socket,不能光看到通过socke API或accept API创建的socket,心中要有图画,这个socket是和远方连接的。每个socket由四元组组成,本地IP,本地端口,远方IP,远方端口。虽然accept函数返回的connection-socket调用getsockname返回的IP和端口同listen-socket绑定的IP和端口是相同的,但因为他们的远方连接的不同,所以他们是不同的socket。不要迷惑。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: