网络篇之 SOCKET 解析

进程间的通信方式

SOCKET是什么

socket起源于Unix,而Unix/Linux中 “一切皆文件”,都可以用“open –> write/read –> close”模式来操作。我认为 Socket也基于该模式的一个实现,socket 打开的是一种特殊的文件
ios协议栈
在这里插入图片描述

socket 鉴于应用层和传输层之间,是linux 统一为应用层提供的一个公共接口。它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议如下图
在这里插入图片描述

SOCKET 相关操作函数简介

socket函数解析

/*
domain: 即协议域,又称为协议族(family)。
常用的协议族有,AF_INET(IPV4)、AF_INET6(IPV6)、AF_LOCAL(或称AF_UNIX,Unix域socket)、
AF_ROUTE(路由套接字用于在内核路由表中添加和删除路由)等等。协议族决定了socket的地址类型,在通
信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX
决定了要用一个绝对路径名作为地址本地进程通信
type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、
SOCK_SEQPACKET等等   
protocol:  故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。protocol
为0时,会自动选择type类型对应的默认协议
protocol 和 type 不能随意搭配,需要根据要求来,如UDP不能用SOCK_STREAM
*/
int socket(int domain, int type, int protocol);

bind 函数解析

在启动的时候都会绑定一个众所周知的地址(将地址按 选定的地址协议族的格式赋给socket。如AF_INET就按IPV4的地址格式 地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。只需要connect()时由系统随机生成一个。

/*
	sockfd:socket描述字,它是通过socket()函数创建了,唯一标识一个socket 
	addr:指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同
	struct sockaddr * 指向
	ipv4
	struct sockaddr_in {
    sa_family_t    sin_family; 
    in_port_t      sin_port;   
    struct in_addr sin_addr;   
	};
	struct in_addr {
	    uint32_t       s_addr;     
	};
	unix域
	#define UNIX_PATH_MAX    108

	struct sockaddr_un { 
	    sa_family_t sun_family;                
	    char        sun_path[UNIX_PATH_MAX];   
	};
	addrlen:对应的是地址的长度
*/
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

listen 函数解析,connect 函数解析

listen:当服务器bind之后,本来socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,设置相关socket fd为监听模式,服务器会监听是否有客户端connect它。
connect:
客户端通过调用connect函数来建立与TCP服务器的连接

/*
sockfd:	为要监听的socket描述字
backflag:	为相应socket可以排队的最大连接个数
*/
int listen(int sockfd, int backlog);
/*
sockfd:		为客户端的socket描述字
addr: 		为服务器的socket地址
addrlen: 	为socket地址的长度
返回值:     0 成功,其他失败
*/
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

accept 函数解析

/*
sockfd:		服务器的socket描述字
addr:		用于返回客户端的协议地址
addrlen: 	协议地址的长度
返回值:		内核自动生成的一个全新的描述字,代表与返回客户的TCP连接
*/
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

读写数据相关函数

调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!网络I/O操作有下面几组:
read()/write()
recv()/send()
readv()/writev()
recvmsg()/sendmsg()
recvfrom()/sendto()

close 函数

int close(int fd);

头文件概述

建议:在编写网络程序时,可以直接使用下面这段头文件代码

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>

三次握手和四次挥手

在这里插入图片描述
为什么要第三次握手?
防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误和开销
防止网络延迟导致客户端第一个syn包到达服务器时候,客户端已经超时关闭连接了,而服务端还以为关联着,导致某些错误和开销

四次挥手,由一端主动释放,可以是客户端也可以是服务端。本次是客户端
在这里插入图片描述
为什么要加一个2ML的time_wait?
当客户端发出最后的ACK确认报文时,并不能确定服务器端能够收到该段报文。所以客户端在发送完ACK确认报文之后,会设置一个时长为2MSL的计时器。MSL指的是Maximum Segment Lifetime:一段TCP报文在传输过程中的最大生命周期。2MSL即是服务器端发出为FIN报文和客户端发出的ACK确认报文所能保持有效的最大时长
挥手时候为什么是四次而不是三次
因为要保证数据完全传输完成 先响应关闭链接,然后发完剩余数据后再次确认关闭

实例

tcp sock代码操作流程
在这里插入图片描述
服务端

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>

#define PORT 8820
#define MAX_LINE 2048
#define LISTENQ 20


int main(int argc , char **argv)
{
	int i,  listenfd, connfd, sockfd;

	ssize_t n, ret;


	char buf[MAX_LINE];

	socklen_t clilen;

	struct sockaddr_in servaddr , cliaddr;

	/*(1) 得到监听描述符*/
	
	listenfd = socket(AF_INET,SOCK_STREAM,0);
	/*(2) 绑定套接字*/
	bzero(&servaddr , sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servaddr.sin_port = htons(PORT);
	bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	/*(3) 监听*/
	listen(listenfd , LISTENQ);
	clilen = sizeof(struct sockaddr_in);
	/*(4) 进入服务器接收请求死循环*/
	while(1){
		printf("wait connect\n");
		if((connfd = accept(listenfd ,(struct sockaddr *)&cliaddr , &clilen)) < 0)
		{
					printf("accept error. %d\n", connfd);
					continue;
		}//if		
 
		printf("accpet a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr) , cliaddr.sin_port);
		while((n = read(connfd , buf , MAX_LINE)) > 0)
		{
			printf("recv msg %s\n",buf);
		}
		printf("recv over\n");
		close(connfd);
	}
	close(listenfd);

}


客户端

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>

#define PORT 8820
#define MAX_LINE 2048
#define LISTENQ 20

int main()
{
	int i,  clientfd;

	ssize_t n, ret;


	char buf[MAX_LINE];

	socklen_t clilen;

	struct sockaddr_in servaddr;

	/*(1) 得到监听描述符*/
	
	clientfd = socket(AF_INET,SOCK_STREAM,0);
	
	bzero(&servaddr , sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servaddr.sin_port = htons(PORT);
	/*(2) 连接到服务器*/
	int fail = connect(clientfd, (struct sockaddr *)&servaddr ,sizeof(servaddr));
	if(fail){
		
		printf("connect fail \n");
		return 0;
	}
	n = write(clientfd, "hello", sizeof("hello"));
	printf("send ok\n");
	
	
}

udp socket 代码操作流

在这里插入图片描述

服务端

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>

#define PORT 8824
#define MAX_LINE 2048
#define LISTENQ 20


int main(int argc , char **argv)
{
	int i,  listenfd;

	ssize_t n, ret;


	char buf[MAX_LINE];

	socklen_t clilen;

	struct sockaddr_in servaddr , cliaddr;

	/*(1) 得到监听描述符*/
	
	listenfd = socket(AF_INET,SOCK_DGRAM,0);
	/*(2) 绑定套接字*/
	bzero(&servaddr , sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servaddr.sin_port = htons(PORT);
	bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));


	while(1){			
		while((n = recvfrom(listenfd , buf, MAX_LINE,0,(struct sockaddr *)&servaddr,sizeof(servaddr))) > 0)
		{
			printf("recv msg %s\n",buf);
		}
		printf("recv over\n");
	}
	close(listenfd);

}


客户端

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <fcntl.h>

#define PORT 8824
#define MAX_LINE 2048
#define LISTENQ 20

int main()
{
	int clientfd;

	ssize_t n;


	struct sockaddr_in servaddr;

	/*(1) 得到监听描述符*/
	
	clientfd = socket(AF_INET,SOCK_DGRAM,0);
	
	bzero(&servaddr , sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	servaddr.sin_port = htons(PORT);
	/*(2) 连接到服务器*/
	int fail = connect(clientfd, (struct sockaddr *)&servaddr ,sizeof(servaddr));
	if(fail){
		
		printf("connect fail \n");
		return 0;
	}
	n = sendto(clientfd , "hhh" ,4,0,(struct sockaddr *)&servaddr,sizeof(servaddr));
	printf("send ok\n");
	
	close(clientfd);
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值