socket编程范式

一、服务器端

1、socket

      socket函数是任何套接口网络编程中第一个使用的函数,制定期望的通信协议类型,返回套接字描述符。程序通过文件描述符访

问文件一样,套接字描述符是访问套接字的一种路径。

listenfd = socket(AF_INET, SOCK_STREAM, 0)


2、bind

      bind函数把一个本地协议地址(32位的IPv4地址或128位的IPv6与16位的TCP或UDP端口号的组合)赋予一个套接字。对于TCP,调用bind函数可以指定一个端口号,或这顶一个IP地址,也可以两者都指定,或都不指定。

     服务器在启动时要绑定它们的中所周知端口,如果一个TCP未调用bind捆绑一个端口,当调用connect或listen时,内核就要为相应的套接字选择一个临时端口。

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


     关于INADDR_ANY:

     INADDR_ANY(通配地址)就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。 一般来说,在各个系统中均定义成为0值。它告知内核去选择IP地址。

     这个宏能够让程序员在不知道本机IP地址的情况下,使用它来代表本机所有接口的IP地址。也就是说,使用这个宏作为监听地址的话,不管本机有多少个接口,socket都会监听。 

      举个例子,假设一个主机有inter1,inter2,inter3三个接口,如果一个socket绑定了INADDR_ANY的地址和8000的端口,那么,从客户端过来的一个UDP包到达该主机,不管客户端connect的是inter1,inter2,inter3中的哪个地址,都会被该socket接收到。如果此时主机还要再建立一个新的socket,使用inter1接口和端口8000,将会失败,因为这个端口和地址已经被第一个socket监听了。


3、listen

int listen(SOCKET sockfd, int backlog);

     仅由TCP服务器调用,它做两件事情

    (1)当socket函数创建一个套接字时,它被假设为一个主动套接字,即一个将调用connect发起连接的客户套接字。而listen函数把一个未连接的套接字转换成一个被动套接字,指示内核应该接受指向该套接字的连接请求。

     (2)backlog指示最大连接数


4、accept

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

     由TCP服务器调用,提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符。新建立的套接字不在监听状态,原来所监听的套接字也不受该系统调用的影响。如果已完成(ESTABLISHED)连接队列为空,那么进程被投入睡眠。

     如果accept成功,那么返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接。addrlen,是个值结果参数,调用函数必须初始化为包含addr所指向结构大小的数值,函数返回时包含对等地址(一般为服务器地址)的实际字节数;

     第一个参数监听套接字,返回值已连接套接字

     监听套接字:一个服务器通常只有一个,在服务器的生命周期内一直存在。

    已连接套接字:内核为每个服务器进程接受的客户连接创界一个已连接套接字(代表对于它的TCP三路握手过程已经完成)。


二、客户端

1、socket同上

(2、bind同上)

3、connect

     TCP客户用conncet函数来建立与TCP服务器的连接

     客户在调用conncet之前不必非得调用bind函数,因为如果需要的话,内核会确定源IP地址,并选择一个临时端口作为源端口。

     对于TCP套接字,调用connect函数将会激发TCP的三路握手过程,而且仅在连接成功或出错时才返回。



实例
server
/*(1)创建监听套接字*/
	if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("create socket error:");
		printf("\n\n");
		exit(1);
	}

	/*初始化服务器的监听套接字地址结构*/
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(PORT); //8889

	/*(2)绑定监听套接字与其地址结构*/
	if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
	{
		perror("bind error:");
		printf("\n\n");
		exit(1);
	}

	/*(3)开启监听*/
	if (listen(listenfd, LISTENEQ) < 0)
	{
		perror("listen error:");
		printf("\n\n");
		exit(1);
	}

	/*初始化已连接描述符数组*/
	maxfd = listenfd; //待测试的描述符的个数
	maxi = -1; //client_sockfd数组的索引
	for (i=0;i<FD_SETSIZE;++i)
	{
		client_sockfd[i] = -1;
	}
	/*初始化allset描述符集*/
	FD_ZERO(&allset);
	FD_SET(listenfd, &allset);

	/*接收客户连接请求*/
	for (;;)
	{
		rset = allset; 
		nready = select(maxfd+1, &rset, NULL, NULL, 0);

		/*测试listenfd是否在rset中,即有连接到达listenfd,此处只处理连接*/
		if (FD_ISSET(listenfd, &rset))
		{
			/*(4)接收客户端的连接请求,分配一个已连接描述符*/
			clilen = sizeof(cliaddr);
			if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) < 0)
			{
				perror("accpet error:");
				printf("\n\n");
				exit(1);
			}
                //略...............

		}

		//略.............
	}

client
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("socket error");
		exit(1);
	}

	//(2)设置服务器的套接字地址
	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(PORT);
	if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0)//转换字符串到网络地址
	{
		printf("inet_pton error for %s\n", argv[1]);
		exit(1);
	}

	//(3)发送服务器连接请求
	if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
	{
		perror("connect error");
		exit(1);
	}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值