套接字socket函数与绑定信息bind函数
套接字
- 套接字是网络编程中的一种通信机制,是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
进程间通信的方式:
1、管道(包括无名管道和命名管道);
2、消息队列;
3、信号量;
4、共享存储。
5、……( Socket和Streams支持不同主机上的两个进程IPC)。
绑定信息(绑定IP和端口)
- 所谓绑定(bind)是指别人连接我只能通过我所绑定的端口,相当于,我买了一个手机,别人要想联系我,必须要知道我的手机号码,这时候,我需要怎么办呢?我需要给手机插上电话卡,固定一个电话号码,这样别人就能通过这个电话号码联系我。手机插上电话卡,固定一个电话号码,类似于绑定(bind)的过程,绑定(bind)为了固定一个端口号,别的网络程序就可以通过IP找到这个端口号,找到这个端口号就能找到这个端口号所对应的网络应用程序。
socket函数
int socket(int domain,int type,int protocol) ;
-
domain :地址域 指定网络层到底使用什么协议
AF_INET : ipv4版本的ip协议
AF_INET6 : ipv6版本的ip协议 -
type :套接字类型
SOCK_DGRAM :用户数据报套接字
SOCK_STREAM :流式套接字 -
protocol:协议
SOCK_DGRAM :
1.指定为0,表示采用默认协议,SOCK_DGRAM默认协议就是UDP协议
2.IPPROTO_UDP ( 17)scoK_STREAM:
1.指定为0,表示采用默认协议,
SOCK_STREAM:
1.指定为0,表示采用默认协议,默认协议是TCP协议
2.IPPROTO_TCP (6) -
返回值: 返回一个套接字操作句柄,本质上是一个文件描述符,返回值大于等于o 则为创建成功,返回值小于0,则为创建失败
bind函数
int bind(int sockfd,const struct sockaddr *addr , socklen_t addr1en);
- sockfd :套接字描述符(socket函数的返回值)
- addr :告诉操作系统内核,当前进程要绑定的地址信息是啥
- addrlen :绑定的地址信息长度是多少
struct sockaddr结构体的组成:
struct sockaddr{
sa_family_t sa_family;//地址域,(AF_INET,AF_INT6)2字节char
sa_data[14];//14字节
}
- sockaddr在头文件#include <sys/socket.h>中定义,sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了
- 注意:
1.struct sockaddr这个结构体是一个通用的地址信息结构,并不是某一个具体的地址信息结构
struct sockaddr_in结构体的组成:
- sockaddr_in在头文件#include<netinet/in.h>或#include<arpa/inet.h>中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中
总结:
在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数。
编写socket函数与bind函数
#inclue<stdio.h>
#include<unistd.h>
//网络编程
#include<sys/socket.h>
#include<netinet/in.h>//互联网地址簇
#include<arpa/inet.h> //信息转化
int main() {
//1.创建套接字
int sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(sockfd<0) {
perror("socket false");
return -1;
}
//2.绑定地址
struct sockaddr_in addr;
addr.sin_family=AF_INET;
//将ip地址转化为无符号32位
//将unit32转化为主机字节序转化为网络字节序
//htonl只能完成第二件事情
//将字符串转化为网络字节序
addr.sin_addr.s_addr=inet_addr("192.168.21.128");
//端口转换为网络字节序
addr.sin_port=htons(19999);
//绑定地址信息
int ret=bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));
if(ret<0) {
perror("bind false");
return -1;
}
while(1) {
sleep(1);
}
return 0;
}
运行结果:sockfd的文件描述符为3,且为一个软连接,闪烁表示源文件不存在,我们之前在管道中也遇见过,因为是直接进入内核内部的缓冲区进操作的,缓冲区并没有文件:
netstat -anp| grep 【端口号】
查看绑定端口的相应信息:
- udp:当前19999端口是udp所占用
- 192.168.21.128:19999:当前服务端监听的ip和端口
- 0.0.0.0:* :表示能接收任意ip和任意地址
- 3760:进程号
- ./test_socket:进程信息
- 当前19999端口已经被3760进程所占用
注意:
1.一个进程可以绑定多个端口,但一个端口只能绑定一个进程
2.如果客户端想绑定端口号,一定要调用发送信息函数之前绑定( bind )端口,因为在发送信息函数( sendto, 或 write ),系统会自动给当前网络程序分配一个随机端口号,这相当于随机绑定了一个端口号,这里只会分配一次,以后通信就以这个随机端口通信,我们再绑定端口号的话,就会绑定失败。如果我们放在发送信息函数( sendto, 或 write )之前绑定,那样程序将以我们绑定的端口号发送信息,不会再随机分配一个端口号。