The so_linger in the socket controls close() to close the socket.
when l_onoff is set to 0, the socket discards the data in the buffer and returns it when close is called;
When l_onoff is set to 1 and l_linger is set to 0, the socket returns an rst message to the other end if there is data in the buffer;
When l_onoff is set to 1 and l_linger is greater than 0, if data exists in the buffer, it will wait for the size of l_linger and then return.
But this doesn't seem to be the case when testing the so_linger parameter.
When I was researching sockets and looking at the SO_LINGER parameter, I ran the following test:
/* server.cc */
#include <arpa/inet.h>
#include <netdb.h>
#include <strings.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
void service(int port = 11115) {
// create address struct
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
std::cout << "create service socket address successed." << std::endl;
// create socket fd
int serv_fd;
if ((serv_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
std::cerr << "socket failed." << std::endl;
return;
}
std::cout << "create service socket: " << serv_fd << std::endl;
// modify receive buffer size
int recv_buf_size = 2048;
socklen_t sz_recvbuf_opt = sizeof(recv_buf_size);
setsockopt(serv_fd, SOL_SOCKET, SO_RCVBUF, (void*)&recv_buf_size,
sz_recvbuf_opt);
std::cout << "set receive buffer size successful." << std::endl;
// bind address info to socket
bind(serv_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
// listen the socket
if (listen(serv_fd, 5) == -1) {
std::cerr << "listen failed." << std::endl;
close(serv_fd);
return;
}
int clnt_fd;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_len = sizeof(clnt_addr);
std::stringstream ss;
char buf[256];
while (1) {
// accept connection fd
clnt_fd = accept(serv_fd, (struct sockaddr*)&clnt_addr, &clnt_addr_len);
ss << "client fd: " << clnt_fd << " | " << clnt_addr.sin_family << " | ";
ss << inet_ntoa(clnt_addr.sin_addr) << " | " << clnt_addr.sin_port
<< std::endl;
std::cout << ss.str();
// read data from client
char ch;
int cnt = 0;
std::cout << "Please enter flag to read data:" << std::endl;
std::cin >> ch;
char readbuf[1025];
readbuf[1024] = 0;
while ((cnt = read(clnt_fd, readbuf, 1024)) > 0) {
std::cout << "read successful." << readbuf << std::endl;
std::cout << "Enter flag to continue read:";
std::cin >> ch;
}
std::cout << "read end.Please enter flag to close client fd" << std::endl;
close(clnt_fd);
}
// close scoket fd
close(serv_fd);
}
int main(int argc, char** argv) {
if (argc > 1) {
std::cout << "port: " << argv[1] << std::endl;
int n_port = atoi(argv[1]);
service(n_port);
} else {
service();
}
return 0;
}
/* client.cc */
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>
#include <string>
int deft_loop_cnt = 4;
void client(const char* serv_ip, int serv_port = 11115) {
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(serv_port);
struct hostent* phost;
if ((phost = gethostbyname(serv_ip)) == nullptr) {
std::cerr << "gethostbyname failed." << std::endl;
return;
}
memcpy(&serv_addr.sin_addr, phost->h_addr, phost->h_length);
int clnt_fd;
// create client socket
if ((clnt_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
std::cerr << "socket failed." << std::endl;
return;
}
std::cout << "create socket successful." << std::endl;
// modify send buffer size
int snd_buf_size = 1024;
socklen_t opt_size = sizeof(snd_buf_size);
setsockopt(clnt_fd, SOL_SOCKET, SO_SNDBUF, (void*)&snd_buf_size, opt_size);
socklen_t sz_linger_opt = sizeof(struct linger);
struct linger new_linger {
0, 0
};
setsockopt(clnt_fd, SOL_SOCKET, SO_LINGER, (void*)&new_linger, sz_linger_opt);
// connection socket
if (connect(clnt_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
std::cerr << "connect failed." << std::endl;
close(clnt_fd);
return;
};
std::cout << "connect to the service successful." << std::endl;
int n;
char buf[1024];
// write information for test send buffer size
for (int i = 0; i < deft_loop_cnt; i++) {
// generate message
for (int j = 0; j < 1024; j++) buf[j] = 'a' + i;
if ((n = write(clnt_fd, buf, sizeof(buf))) < 0) {
std::cout << "write error: " << n << std::endl;
goto close_fd;
}
std::cout << "write 1024 bytes successfuls: " << i << std::endl;
}
// don't need last message
close_fd:
close(clnt_fd);
}
int main(int argc, char* argv[]) {
if (argc < 2) return 0;
std::cout << "start client " << argv[1] << "......" << std::endl;
if (argc > 2) {
int n_port = atoi(argv[2]);
std::cout << "client port: " << n_port << std::endl;
client(argv[1], n_port);
} else {
client(argv[1]);
}
return 0;
}
At this time, according to google about so_linger document,
when linger.l_onoff is set to 0, after calling close, socket will discard all the information in the receive and send buffers.
However, by checking with "ss -tm", I found that the client program is still waiting for the server to accept the data!
Shouldn't it discard the data in the buffer? Why doesn't it discard the data in the buffer?
That is not entirely correct. You are disabling the linger option by setting
l_onoffto 0, soclose()will not discard the buffer.Per the documentation:
https://man7.org/linux/man-pages/man7/socket.7.html
Also see Resetting a TCP connection and SO_LINGER, which goes into a rather lengthy explanation of how
SO_LINGERactually behaves.