Linux高阶——1116—SOCKET套接字基础
1、SOCKET套接字基础
操作系统提供,用于开发网络应用的一系列API函数,我们称之为套接字函数
套接字函数适用于各个语言和平台,除了API可以不同,其他概念理论或流程逻辑都相同
1、在Linux操作系统中,sock为整型,也称int sockfd,sock描述符,后续网络通信都是用此描述符
2、int sockfd,包含网络信息,默认下包含默认地址0.0.0.0与随机端口,因此sockfd创建后可以立即使用
3、int sockfd,套接字描述符,后续用于数据传输,recv(int sock),send(sockfd),sendto(sockfd),recvfrom(sockfd)
4、struct socksddr_in网络信息结构体
2、socket函数
1、socket()——socket创建
int sockfd=socket(AF_INET|AF_INET6,SOCK_STREAM|SOCK_DGRAM,0);
第一个参数——指定使用的socket协议
第二个参数——传输层协议,TCP或UDP
返回值——成功返回整型的(大于0的)socket,失败返回-1
当刚创建时,socket包含的ip为本机任意ip,包含的端口号为随机端口号,因此刚创建出来时就可以使用
因此客户端的sockfd创建以后,无需绑定,也可以使用,因为里面含有本机任意ip和端口号
2、bind()——socket绑定
int value=bind(int sockfd,struct sockaddr* addr,socklen_t addrlen);
bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));
第一个参数——int类型的socket
第二个参数——网络信息结构体的地址
第三个参数——网络信息结构体的长度
返回值——成功返回0,不成功返回-1
sockfd里可以自定义网络信息(指定ip和port),使用bind对int sockfd中的网络信息进行设置自定义
固定信息,一般只有服务端需要,大多数服务端必须绑定,保证客户端能够找到服务端,客户端随意
大多数时间udp都需要bind,而tcp只有服务端需要绑定
3、listen()——监听网络连接状态
listen(int sockfd,int backlog=128);
第一个参数——需要监听的socket
第二个参数——监听序列
返回值——成功返回0,不成功返回-1
监听连接状态,一般只有TCP协议使用
TCPIP协议的后台有双队列,第一个队列存储的为等待三次握手的socket,第二个队列存储握手成功的socket
等待三次握手的队列不能过短:因为如果等待握手的socket的数量超过了等待握手队列的长度,则下个将要等待连接的socket会被服务器主机将RST置0(复位:让客户端重连,如果客户端不重连,连接将会丢失)
队列不能过长:浪费内存资源
定位128的队列长度可以保证过万的连接
4、accept()——执行此函数等待连接,并完成握手(服务端使用)
如果存在一个服务端,有20000个客户端连接此服务端,此时服务器软件内存在多少个socket?
答:存在20001个,存在1个socket是专门用于请求连接的,其余20000个均为连接成功后,客户端的用于传输数据的socket
用于连接的socket
int socket_sock;
用于传输的socket——计算进程sock最大值,sock最大值=连接数量+1
int client_sock;
accept是经典的阻塞连接函数
client_sock=accept(server_sock,struct sockaddr* client_addr,&addrlen);
第一个参数——服务端socket
第二个参数——客户端连接成功,成功后将客户端的网络信息结构体传出到client_addr中,获得客户端的ip和端口号
第三个参数——传入传出信息长度,传入能够接收的长度,传出结构体实际接收到多大的长度
socklen_t addrlen;
addrlen=sizeof(client_addr);
需要注意:每次连接前都要初始化addrlen,否则能够输入的大小会越来越小
返回值——指向客户端的socket——client_sock
client_sock存储指向客户端的sock,后续与客户端数据通信使用它
5、connect()——通过此函数对目标请求tcp连接(客户端使用)
connect(int sockfd,struct sockaddr* destaddr,sizeof(destaddr));
第一个参数——连接的socket(客户端只有一个socket)
第二个参数——目标信息结构体
第三个参数——目标信息结构体的长度
阻塞请求连接,成功返回0,失败返回-1
6、recv()读取指定sockfd中的数据包
ssize_t wlen=send(int sockfd,void* buf,strlen(buf),MSG_NOSIGNAL);
第一个参数——发送目标的sockfd
第二个参数——数据buf
第三个参数——数据长度size
第四个参数——选项
返回值——发送的长度
7、send()向sockfd发送数据包
ssize_t rlen=recv(int sockfd,void* buf,size(buf),MSG_DONTWAIT);
第一个参数——发送的人的sockfd
第二个参数——数据buf
第三个参数——数据长度size
第四个参数——选项flags=0—阻塞读,MSG_DONTWAIT—非阻塞读
返回值——发送的长度
3、客户端代码
#include<sys/socket.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main()
{
struct sockaddr_in server_addr;
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
server_addr.sin_addr.s_addr = inet_addr("82.157.31.74");
int client_sock;
if((client_sock=socket(AF_INET,SOCK_STREAM,0))==-1)
{
perror("sock create failed");
return -1;
}
socklen_t addrlen;
printf("TCP IO Client Running...\n");
addrlen = sizeof(server_addr);
if((connect(client_sock, (struct sockaddr*)&server_addr, addrlen))==-1)
{
perror("failed");
close(client_sock);
return -1;
}
char buffer[1024];
recv(client_sock, buffer, sizeof(buffer), 0);
printf("Received from server: %s\n", buffer);
close(client_sock);
}