在网络里面,每一个节点(计算机或路由器)都有一个网络地址,也就是IP地址,两个进程通信时,首先要确定各自所在的网络节点的网络地址,网络地址只能确定进程所在的计算机,一个计算机可能同时运行多个进程,为了确定要通信的进程,仅仅有网络地址还是不够的,套接字还要其他信息,端口号。一台计算机里,一个端口号一次只能分配给一个进程,端口号和进程之间是一种一一对应关系。端口号只有本地意义,不同计算机上的相同的端口号是没有什么联系的。一些端口号是固定早就被分配了的。一个新的应用程序出现,必须给它指派一个“周知"的端口号,否则其他的应用程序进程就无法和它进行交互。还有一些一般的端口号是随时分配给请求通信的客户进程的。</span>
套接字有两个
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
这是所说的通用套接字,由于历史原因,反正我也不知道什么原因,特定与某种协议的套接字结构指针都要强制转换为通用套接字指针,以实现协议无关性。
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
这个是IPv4的套接字,与通用套接字不同的是,sockaddr_in将ip地址和端口号分开为不同的成员,套接字的通信地址类型有很多,在socket.h里面,IPv4对应的是AF_INET,IPV6对应的是AF_INET6。一般用到的就是这两个。
socket函数为套接字在sockfs文件系统中分配一个新的文件和dentry对象,并通过文件描述符把他们与调用进程联系起来,进程可以像访问一个已经打开的文件一样访问套接字
在sockfs中的对应文件,但进程不能调用open来访问文件,因为sockfs文件系统没有可视安装点,其中的文件永远不会出现在系统目录树上,当套接字关闭时,内核会自动删
除scokfs的inodes。
/*本机即做服务器又做客户端测试TCP链接,运行程序后在浏览器输入http://localhost:9000/,shell窗口会显示新的ID*/
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<string.h>
#define PATH_MAX 128 //主机名数组
#define PORT 9000
int main()
{
int sockfd,newsockfd;/*定义两个套接字描述符*/
struct sockaddr_in addr;/*IPv4类型的套结字地址数据结构*/
int addr_len=sizeof(struct sockaddr_in);//记录结构大小,后面会作为参数*/
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)//创建一个套结字*/
{
//地址协议族IPv4,套结字类型是流套结字*/
perror("socket");
exit(0);
}
else
{
printf("creat socket success!sockid :%d\n",sockfd);
}
bzero(&addr,sizeof(struct sockaddr_in));//先清空结构
addr.sin_family=AF_INET;//设置地址协议族
addr.sin_port= htons(PORT);//端口号
addr.sin_addr.s_addr=htonl(INADDR_ANY);//ip地址是本机地址
if(bind(sockfd,(struct sockaddr *)(&addr),sizeof(struct sockaddr))<0)
{
//把刚刚创建好的套结字和端口绑定
perror("bind");
exit(0);
}
else
{
printf("bind success!\n");
}
if(listen(sockfd,5)<0)//开始监听允许最大请求树是5
{
perror("listen");
exit(1);
}
else
{
printf("listening.....\n");
}
if((newsockfd=accept(sockfd,(struct sockaddr *)(&addr),&addr_len))<0)
//调用accept接受一个请求连接,成功后会返回一个新的套结字描述符号以后可以用这个新的通信
{
perror("accept");
exit(2);
}
else
{
printf("accept a new connection.new socket id: %d\n",newsockfd);
}
close(sockfd);
return 0;
}