网络之Socket详解
1.什么是Socket?
Socket本质上还是文件,因为Linux上一切皆文件。Socket也有对应的文件描述符(fd)。文件描述符相关的参考另外一篇博客。
http://blog.csdn.net/weililansehudiefei/article/details/78113082
在这里简单就认为,它是对应着一个文件的,就可以。
Socket位于TCP/IP之上,通过Socket可以方便的进行通信连接。对外屏蔽了复杂的TCP/IP。
2.Socket连接详解
Socket连接建立的基本过程如下:
接下来就按照这个流程进行
1.int socket(int domain,int type,int protocol);
该方法 返回一个socket_fd,即socket的文件描述符
domain:说明我们网络程式所在的主机采用的通讯协族(AF_UNIX和AF_INET等). AF_UNIX只能够用于单一的Unix系统进程间通信,而AF_INET是针对Internet的,因而能允许在远程主机之间通信(当我们 man socket时发现 domain可选项是 PF_*而不是AF_*,因为glibc是posix的实现所以用PF代替了AF,不过我们都能使用的).
type:我们网络程式所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM等)SOCK_STREAM表明我们用的是TCP协议,这样会提供按顺序的,可靠,双向,面向连接的比特流. SOCK_DGRAM 表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信.
protocol:由于我们指定了type,所以这个地方我们一般只要用0来代替就能了 socket为网络通讯做基本的准备.成功时返回文件描述符,失败时返回-1,看errno可知道出错的周详情况.
2.int bind(SOCKET socketfd, const struct sockaddr* address, socklen_t address_len);
sockfd:是由上一步socket调用返回的文件描述符.
addrlen:是sockaddr结构的长度.
my_addr:是个指向sockaddr的指针.在<linux/socket.h>中有 sockaddr的定义
struct sockaddr{
unisgned short as_family;
char sa_data[14];
};
不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct sockaddr_in)来代替.在<linux/in.h>中有sockaddr_in的定义
struct sockaddr_in{
unsigned short sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
我们主要使用Internet所以sin_family一般为AF_INET,sin_addr设置为INADDR_ANY表示能和所有的主机通信,sin_port是我们要监听的端口号.sin_zero[8]是用来填充的. bind将本地的端口同socket返回的文件描述符捆绑在一起.成功是返回0,失败的情况和socket相同
3.int listen(int socketfd,int backlog);
ilsten通过socket套接字和该套接字绑定的IP信息在内核开启监听,并且返回监听描述符。此处监听工作交给内核处理,代码本身不阻塞,但内核对应端口一直在做监听工作。同时维护两个队列,一个是请求队列,一个是就绪队列,大小由backlog决定。
4.int accept(int sockfd,struct sockaddr *addr, socklen_t *addrlen);
接受连接请求,代码默认阻塞。accept实际上是在从内核listen维护的就绪队列中取描述符。accept成功时返回最后的服务器端的文件描述符。重要:这里返回的是一个新的Socket,即socketfd_new.而不是最开始创建的socketfd。用socket_new进行与客户端的通信。
服务器的一个端口,是只能有一个socket绑定的。新生成的socketfd_new虽然有和处理连接请求的socket一样的ip和端口,但是他门不占用端口。我的理解,就是这些新生成的socketfd_new,没有进行bind的操作,所以没有和内核绑定,所以没出现冲突。而socketfd本质上有五大因素决定(fd,src_ip,src_port,des_ip,des_port)。socket里是存放了双方的ip的。
所以,accept方法返回的socketfd_new,已经有了双端的ip和端口,那么久可以进行通信了。
那么,当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?
客户端发送过来的数据可以分为2种,一种是连接请求,一种是已经建立好连接后的数据传输。
由于TCP/IP协议栈是维护着一个接收和发送缓冲区的。在接收到来自客户端的数据包后,服务器端的TCP/IP协议栈应该会做如下处理:如果收到的是请求连接的数据包,则传给监听着连接请求端口的socetfd套接字,进行accept处理;如果是已经建立过连接后的客户端数据包,则将数据放入接收缓冲区。这样,当服务器端需要读取指定客户端的数据时,则可以利用socketfd_new 套接字通过recv或者read函数到缓冲区里面去取指定的数据(因为socketfd_new代表的socket对象记录了客户端IP和端口,因此可以鉴别)。
5.int connect(int sockfd,const struct sockaddr *addr, socklen_t addrlen)
sockfd:socket:客户端socket返回的文件描述符.
serv_addr:储存了服务器端的连接信息.其中sin_add是服务端的地址
addrlen:serv_addr的长度
connect函数是客户端用来同服务端连接的.成功时返回0,sockfd是同服务端通讯的文件描述符失败时返回-1
原文:https://blog.csdn.net/weililansehudiefei/article/details/78111569