tcp/ip网络编程--accept()函数返回的套接字

tcp/ip网络编程–accept()函数返回的套接字

套接字:1)套接字是对网络中不同主机的应用进程之间进行双向通信的端点的抽象一个套接字就是网络进程通信的一端。[1]
2)套接字是用来与另一个进程跨网络通信的文件[2]
套接字表示:IP地址,端口号,传输层协议(tcp/udp)三元组构成
其实套件字还包含各种状态跟属性等等,核心是三元组。

文件描述符:文件描述符是一个简单的非负整数,用来表示一个被进程打开的文件,每个进程都至少有三个文件描述符[2]
0——>stdin 标准输入文件
1——>stdout 标准输出文件
3——>stderr 标准错误文件
文件类型:每个Linux文件都有一个类型,表明在系统中的角色[2]。
1)普通文件
2)目录文件
3)套接字
4)其他。。。。

套接字描述符:从前面的描中,我们可以将套接字描叙符简单的理解为一种文件描述符。

tcp连接:tcp连接由一个四元组构成,它们分别是两个IP地址,两个端口号。也就是说,一个tcp连接是由一对套接字构成,[3].
socket_1{本地IP,本地端口号,TCP协议}
socket_2{外地IP,外地端口号,TCP协议}

accept(int sockfd,struct sockaddr * restrict addr,socklen_t * restrict addrlen);
//restrict 是一个关键字,在Linux中为_restrict,这个关键字只可用于指针,表明该指针是访问一个数据对象的唯一且初始的方式。
//作用:允许编译器对同类型代码进行合并并优化,提高编译效率

第一个参数sockfd是一个被socket()创建后被bind()然后listen()的处于LISTEN 状态的套接字。
第二参数addr是一个struct sockaddr 类型的指针,这个结构体会被填充外地套接字的地址,这个地址在传输层被知道。
第三个参数addlen是一个value-result参数
(value-result 参数:当函数被调用时,一个参数作为一个值从函数外传入函数内,当函数返回时,这个参数又存储了函数执行的部分结果。总是以指针/引用的方式传递)
调用者必须用addr指向的结构体的大小初始化它,函数返回时,它将包含外地地址的实际大小。

return value :
成功的话返回一个非负整数,作为已接收的套接字的文件描述符,错误则返会-1。

利用下面两个函数查询套接字信息

int getsockname(int sockfd, struct sockaddr *localaddr,socklen_t *addrlen);
//查询与套接字关联的本地地址,本地端口号
int getpeername(int sockfd, struct sockaddr *peeraddr,socklent_t *addrlen);
//查询与套接字关联的外地地址,外地端口号

下面是代码实现:

//服务器端,运行在linux虚拟机上,虚拟机的IP地址固定为192.168.79.212
#include <cstdio>
#include<cstdlib>

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


#define PORT 6563
#define LISTENQ 1024

using std::cout;
using std::endl;
int main() {
    int listen_fd, clnt_fd;//两个套接字描述符,分别关联listen套接字和连接套接字
    struct sockaddr_in serv_adr, clnt_adr, temp_adr;
    socklen_t sock_len=sizeof(serv_adr),clnt_len=sizeof(clnt_adr);

    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_port = htons(PORT);//绑定端口号
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);

    bind(listen_fd, (sockaddr*)&serv_adr,sizeof(serv_adr));
    getsockname(listen_fd, (struct sockaddr*)&temp_adr, &sock_len);
    cout << "利用getsockname()查询绑定后的 serv_sock ip " << 
        inet_ntoa(temp_adr.sin_addr) << ":" << ntohs(temp_adr.sin_port)<<endl;

    listen(listen_fd, LISTENQ);//serv_sock变为listen态
    getsockname(listen_fd, (struct sockaddr*)&temp_adr, &sock_len);
    cout << "利用getsockname()查询listen()后的 listen_sock ip " <<
        inet_ntoa(temp_adr.sin_addr) << ":" << ntohs(temp_adr.sin_port) << endl;

    clnt_fd=accept(listen_fd, (struct sockaddr*)&clnt_adr, &sock_len);
    getsockname(clnt_fd, (struct sockaddr*)&temp_adr, &clnt_len);
    cout << "利用getsockname()查询accept()返回的 clnt_sock ip " << 
        inet_ntoa(temp_adr.sin_addr) << ":" << ntohs(temp_adr.sin_port)<<endl;
    getpeername(clnt_fd, (struct sockaddr*)&temp_adr, &clnt_len);
    cout << "利用getpeername()查询accept()返回的 clnt_sock ip " << 
        inet_ntoa(temp_adr.sin_addr) << ":" << ntohs(temp_adr.sin_port) << endl;
 close(clnt_fd);
 close(listen_fd);
 return 0;
 }
 
//客户端,在Windows主机上运行 主机IP地址为192.168.79.1
#include<winsock2.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<iostream>
using std::cout;
const char* argv[] = { "hclientwin","192.168.79.212","6563" };

int main()
{
	WSADATA wsaData;
	SOCKET hSocket;
	SOCKADDR_IN serAddr;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	hSocket = socket(PF_INET, SOCK_STREAM, 0);

	memset(&serAddr, 0, sizeof(serAddr));
	serAddr.sin_family = AF_INET;
	serAddr.sin_addr.s_addr = inet_addr(argv[1]);
	serAddr.sin_port = htons(atoi(argv[2]));

	connect(hSocket, (SOCKADDR*)&serAddr, sizeof(serAddr));
	puts("connected ...");
	SOCKADDR_IN serv, guest;
	int serv_sz, guest_sz;
	serv_sz = sizeof(serv);
	guest_sz = sizeof(guest);
	getsockname(hSocket, (sockaddr*)&guest, &serv_sz);
	cout << "connected using getsockname() get port :" <<
		ntohs(guest.sin_port) << std::endl;
	getpeername(hSocket, (sockaddr*)&serv, &serv_sz);
	cout << "connected using getpeername() get port :" <<
		ntohs(serv.sin_port) << std::endl;
	closesocket(hSocket);
	WSACleanup();
	return 0;
}

结果:
服务器显示结果

windows 控制台显示结果
分析:可以看到服务器端的accept()返回的连接套接字的本地IP为虚拟机自己的IP,本地端口号为我们提前设置的端口号,外地IP为windows 主机IP号,外地端口为64131、由内核动态生成,每次运行都会有不同的结果。
可以看出在accept()函数返回后,tcp连接已经建立,具备一个四元组,一对套接字。

如果发现错误,或者由什么建议,欢迎在下面评论指出,谢谢你的观看
如果发现错误,或者由什么建议,欢迎在下面评论指出,谢谢你的观看
如果发现错误,或者由什么建议,欢迎在下面评论指出,谢谢你的观看

[1]百度百科-套接字
[2]深入理解计算系统(cssap),机械工业出版社,第三版,第10章 系统级IO,622,623页。
[3]TCP/IP详解[卷1],机械工业出版社,第二版,第13章 TCP连接管理,595页。
[4]accept(2) — Linux manual page
[5]value-result参数
[6]getsockname函数和getpeername函数

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值