socket、sockaddr_in和sockaddr的区别
在实现简单的服务器端和客户端的通信中,对于服务器端来说,通信的过程是:
- 通过socket函数来创建套接字(相当于网络接口)
- 调用bind函数为其套接字分配ip地址和端口号
- 调用listen函数将套接字转为可接受连接的状态
- 调用accept函数来受理可能发来的连接请求
- 调用write函数来传输数据
对于客户端来说,通信的过程概括为如下:
- 通过socket函数来创建套接字(相当于网络接口)
- 调用connect函数,使之成为客户端套接字,向服务器端发出连接请求
客户端和服务器端都有创建套接字的过程,是通过第二步调用的函数才能确定这是属于服务器端的socket还是客户端的socket。socket创建之后相当于linux中的文件描述符(此处的定义和打开文件一样,相当于分配了一个编号)
以客户端的代码为例,创建socket的过程大致如下:
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len;
sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock == -1)
error_handling("socket error()");
//此时只创建了一个socket,但是还没有分配
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_Addt(argv[1]); //argv是命令行参数的数组
serv_addr.sin_port = htons(atoi(argv[2]));
其中出现了socket、sockaddr_in、以及sock_addr的概念,故对此做一些辨析。
1. socket
socket即套接字,在网络编程中相当于是在端对端的通信中创建了一种网络接口,一般是通过ip传输到指定的主机,但是主机可能同时有视频、网页等等多个进程需要建立网络连接,因此操作系统收到socket之后通过端口号来区分下一步需要分配给哪个端口对应的进程(tcp和udp可以用同一个端口号)
因此,socket本质上是进行通信的最终载体。
socket创建的时候通过socket()函数来进行,第一个参数表示选择协议族信息,pf_inet表示是ipv4协议族,第二个参数表示数据传输方式,这里表示字节流的方式,即TCP。第三个参数是指定具体协议信息。
在通信结束之后,需要调用close函数来关闭套接字。
2. sockaddr_in
sockaddr_in是结构体类型,定义如下:
struct sockaddr_in
{
sa_family sin_family; //地址族
uint16_t sin_port; //16位tcp/udp端口号
struct in_addr sin_addr; //32位ip地址,in_addr同样是结构体变量
char sin_zero[8]; //不使用 共8字节
}
为了给socket命名和使其使用,服务器需要给socket绑定对应的ip和端口号,这样socket才可以投入使用。
3. sock_addr
sock_addr是sockaddr_in的一种集成,对于sockaddr_in来说,包含所有的端口号、ip、地址族等信息,而sockaddr只有sa_family以及char sa_data[14];
sa_data[14]集成了sockaddr_in里面所有的信息。