socket-几点
1.
tcp连接,服务器端的accept返回的套接字怎么和客户端对应的套接字通信?
tcp server端进行的动作有
//创建STREAM socket
int server_sockfd;
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
//想要监视的地址及端口
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
server_address.sin_port = 9734;
//绑定
bind(server_sockfd, (struct sockaddr *)&server_address, sizeof(server_address));
//创建队列,此处可以允许两个客户同时连接
listen(server_sockfd,2);
//阻塞在此处,直到有客户请求连接
int client_sockfd;
struct sockaddr_in client_address;
client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &sizeof(client_address));
//阻塞在此处,直到该客户有写数据到client_sockfd
read(client_sockfd, &ch, 1);
ch++;
write(client_sockfd, &ch, 1);
tcp client端进行的动作有
//创建socket
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
//想要连接的服务器地址和端口
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = 9734;
//连接
connect(sockfd, (struct sockaddr *)&address, len);
//写数据
write(sockfd, &ch, 1);
read(sockfd, &ch, 1);
当服务器在while中循环监听(accept)时,每有一个客户端连接,服务器端的accept都会有建立一个新的套接字描述符(即一个int数字)来代表当前的这个连接.如下
The file descriptor returned by accept is a socket descriptor that is connected to the client that called connect. This new socket descriptor has the same socket type and address family as the original socket (sockfd). The original socket passed to accept is not associated with the connection, but instead remains available to receive additional connect requests.--apue2-16.4
假设server已经在192.168.1.110上运行起来,
在192.168.1.111上启动client A,假设client A创建的套接字描述符是10,
当client A进程执行connect操作时,Server会accept,并返回一个新的套接字描述符比如5表示这个连接。那么5究竟怎么唯一表示这个连接呢?
当client A进程执行write(sockfd, &ch, 1)时,其发出的tcp包首部元素中,源端口号是12345(其实没有指定,os任意选择一个未使用的tcp端口号),目的端口号是9734。
当server进程执行write(client_sockfd, &ch, 1),其发出的tcp包首部中,源端口号是9734(必须的,服务器已经绑定这个端口,其写数据时会将9734塞进tcp包首部的源端口号域中),目的端口号是123456
假设客户端又启动一个进程Client B,假设Client B创建的套接字描述符是11(与Client A创建的不同,因为套接字描述符同文件描述符一样是全局性的)。假设Server进程为相应Client B的connect而创建的套接字描述符是6。假设os为Client B的套接字11分配的源地址及端口号是192.168.1.111:123457。
即
进程Server向套接字描述符5写入数据时,tcp包中指明的源地址及端口是192.168.1.110:9734,目的地址及端口是192.168.1.111:123457
客户端接收到Server 发来的tcp包时,发现目的端口号是123457,对应自己的clientB进程里的套接字描述符11,便把数据塞进clientB进程的描述符11。
client B向套接字描述符11写数据时,tcp包中指明的源地址及端口是192.168.1.111;123457,目的地址及端口是192.168.1.110:9734
服务器接收到Client A发来的tcp包时,发现目的端口号时9734,对应自己的Server进程里的套接字描述符6,便把数据塞进Server进程的描述符5。
这样Server的描述符6和ClientB的描述符11可以传输数据了。
综上,一个既定的socket描述符确定了源地址及端口,目的地址及端口。
另外,在客户端如果没有绑定源地址及端口,则os自动选择一个默认的ip和未使用的端口作为此socket的源地址及端口。如上。可以同服务器端一样绑定一个ip及端口,使用bind。
2.setsockopt的几个参数
tcp连接,服务器端的accept返回的套接字怎么和客户端对应的套接字通信?
tcp server端进行的动作有
//创建STREAM socket
int server_sockfd;
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
//想要监视的地址及端口
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
server_address.sin_port = 9734;
//绑定
bind(server_sockfd, (struct sockaddr *)&server_address, sizeof(server_address));
//创建队列,此处可以允许两个客户同时连接
listen(server_sockfd,2);
//阻塞在此处,直到有客户请求连接
int client_sockfd;
struct sockaddr_in client_address;
client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &sizeof(client_address));
//阻塞在此处,直到该客户有写数据到client_sockfd
read(client_sockfd, &ch, 1);
ch++;
write(client_sockfd, &ch, 1);
tcp client端进行的动作有
//创建socket
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
//想要连接的服务器地址和端口
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = 9734;
//连接
connect(sockfd, (struct sockaddr *)&address, len);
//写数据
write(sockfd, &ch, 1);
read(sockfd, &ch, 1);
当服务器在while中循环监听(accept)时,每有一个客户端连接,服务器端的accept都会有建立一个新的套接字描述符(即一个int数字)来代表当前的这个连接.如下
The file descriptor returned by accept is a socket descriptor that is connected to the client that called connect. This new socket descriptor has the same socket type and address family as the original socket (sockfd). The original socket passed to accept is not associated with the connection, but instead remains available to receive additional connect requests.--apue2-16.4
假设server已经在192.168.1.110上运行起来,
在192.168.1.111上启动client A,假设client A创建的套接字描述符是10,
当client A进程执行connect操作时,Server会accept,并返回一个新的套接字描述符比如5表示这个连接。那么5究竟怎么唯一表示这个连接呢?
当client A进程执行write(sockfd, &ch, 1)时,其发出的tcp包首部元素中,源端口号是12345(其实没有指定,os任意选择一个未使用的tcp端口号),目的端口号是9734。
当server进程执行write(client_sockfd, &ch, 1),其发出的tcp包首部中,源端口号是9734(必须的,服务器已经绑定这个端口,其写数据时会将9734塞进tcp包首部的源端口号域中),目的端口号是123456
即
进程Server向套接字描述符5写入数据时,tcp包中指明的源地址及端口是192.168.1.110:9734,目的地址及端口是192.168.1.111:123456
客户端接收到Server 发来的tcp包时,发现目的端口号是123456,对应自己的clientA进程里的套接字描述符10,便把数据塞进clientA进程的描述符10。至于在linux下,数据有没有缓冲,怎么缓冲的,缓冲建立在进程空间还是内核空间,还不知道。
client A向套接字描述符10写数据时,tcp包中指明的源地址及端口是192.168.1.111;123456,目的地址及端口是192.168.1.110:9734
服务器接收到Client A发来的tcp包时,发现目的端口号时9734,对应自己的Server进程里的套接字描述符5,便把数据塞进Server进程的描述符5。
假设客户端又启动一个进程Client B,假设Client B创建的套接字描述符是11(与Client A创建的不同,因为套接字描述符同文件描述符一样是全局性的)。假设Server进程为相应Client B的connect而创建的套接字描述符是6。假设os为Client B的套接字11分配的源地址及端口号是192.168.1.111:123457。
即
进程Server向套接字描述符5写入数据时,tcp包中指明的源地址及端口是192.168.1.110:9734,目的地址及端口是192.168.1.111:123457
客户端接收到Server 发来的tcp包时,发现目的端口号是123457,对应自己的clientB进程里的套接字描述符11,便把数据塞进clientB进程的描述符11。
client B向套接字描述符11写数据时,tcp包中指明的源地址及端口是192.168.1.111;123457,目的地址及端口是192.168.1.110:9734
服务器接收到Client A发来的tcp包时,发现目的端口号时9734,对应自己的Server进程里的套接字描述符6,便把数据塞进Server进程的描述符5。
这样Server的描述符6和ClientB的描述符11可以传输数据了。
综上,一个既定的socket描述符确定了源地址及端口,目的地址及端口。
另外,在客户端如果没有绑定源地址及端口,则os自动选择一个默认的ip和未使用的端口作为此socket的源地址及端口。如上。可以同服务器端一样绑定一个ip及端口,使用bind。
2.setsockopt的几个参数
keepAlive = 1;
int keepIdle = 1;
int keepInterval = 1;
int keepCount = 1;
setsockopt(pSocket->m_ClientSock, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));//启用探测
setsockopt(pSocket->m_ClientSock, SOL_TCP,TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));//单位s,当线路出现keepIdle 时间无数据传输时,会发送探测询问
setsockopt(pSocket->m_ClientSock, SOL_TCP,TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));//单位s,探测发送时间间隔
setsockopt(pSocket->m_ClientSock,SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));//单位次,探测循环次数
在线程执行recv/read等时,如果socket没有数据,会阻塞;当阻塞时间过keepIdle后,自动启动探测,此时
如果网线掉落或线路出错,则会返回-1,置错误号110。
如果网线掉落或线路出错,则会返回-1,置错误号110。
如果正常,继续阻塞。