Linux 下socket编程 connect()函数返回-1(error:Connection refused)

一、背景

系统:CentOS7 64位 物理机
IP:192.168.2.199/24
使用端口:9999

二、问题描述

在tty1上运行服务器程序,在tty2上运行客户端程序
若连接成功,则服务器程序会向客户端程序发送“Hello World!”字符串。
服务端正常监听,但是客户端在连接时却连接不上,connect()函数返回-1。

服务端部分代码

int main(int argc, char *argv[])
{
	int serv_sock, clnt_sock;

	struct sockaddr_in serv_addr, clnt_addr;
	socklen_t clnt_addr_size;

	char message[] = "Hello World!";

	if(argc != 2)
	{
		printf("Usage: %s \n", argv[0]);
		exit(1);
	}

	serv_sock = socket(AF_INET, SOCK_STREAM, 0);
	if(serv_sock == -1)
		handling_error("socket() error");

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htonl(atoi(argv[1]));

	if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
		handling_error("bind() error");

	if(listen(serv_sock, 5) == -1)
		handling_error("listen() error");

	clnt_addr_size = sizeof(clnt_addr);
	clnt_sock = accept(serv_sock, (struct sockaddr*) &clnt_addr, &clnt_addr_size);
	if(clnt_sock == -1)
		handling_error("accept() error");

	write(clnt_sock, message, sizeof(message));
	close(clnt_sock);
	close(serv_sock);
	return 0;
}

客户端部分代码

int main(int argc, char *argv[])
{
	int sock;
	struct sockaddr_in serv_addr;
	char message[30];
	int str_len;

	if(argc != 3)
	{
		printf("Usage: %s  \n", argv[0]);
		exit(1);
	}
	
	sock = socket(AF_INET, SOCK_STREAM, 0);
	if(sock == -1)
		error_handling("socket() error");

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
	serv_addr.sin_port = htons(atoi(argv[2]));

	if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
		error_handling("connect() error!");

	str_len = read(sock, message, sizeof(message) - 1);
	if(str_len == -1)
		error_handling("read() error!");

	printf("Message from server: %s \n", message);
	close(sock);
	return 0;
}	

三、问题解决

在上面两部分代码中使用了error_handling自定义函数,为了找出错误原因,调用了perror(const char *message)函数,该函数会打印message字符串,并在其后打印errno变量,Linux中系统调用的错误都存储于 errno中,errno由操作系统维护,存储就近发生的错误,即下一次的错误码会覆盖掉上一次的错误。

error_handling(char *message)函数

void error_handling(char *message)
{
	perror(message);
	exit(1);
}

运行后,客户端报错:connect() error!:Connection refused,即链接请求被拒绝,在看了一遍程序没有发现明显错误后,我想看一下8888端口是否在使用。

lsof -i:8888

结果出来为空,8888端口并没有被使用。然后我再查看服务器进程在使用哪个端口。

netstat -tnlp

然后发现服务端程序正常监听,但是端口不是8888,而是39388,这意味着是操作系统给随机分配了一个端口,而没有使用我们指定的端口。
回到服务端程序,仔细查看初始化套接字的那段代码,找出了错误。
端口为16位,在转换主机字节序和网络字节序的时候使用了htonl(),htonl()函数是进行32位的转换,CentOS7系统采用的字节序为小端字节序。
Linux 下socket编程 connect()函数返回-1(error:Connection refused)_第1张图片
假如采用htonl()函数,则操作系统会选择[0x20, 0x21)段的值作为端口号,因此,htonl(8888)的实际结果为0而非8888的网络字节序。由于端口的实际值为0,这会导致当服务端调用函数listen时,内核会为服务端选择一个临时端口,这个临时端口通常与我们所指定的端口并不相同。
因此将htonl()函数换为htons()函数就能解决这个问题了。

四、总结

引起Connection refused的可能原因:服务端在客户端所请求的端口上没有服务在等待连接。

也许是服务端根本没有启动;也许是服务端启动成功了,但客户端所请求的端口与服务端正在监听的端口不一致(客户端所请求的端口并不一定与我们所指定的一致,服务端正在监听的端口也并不一定与我们所指定的一致,这正是本文所讨论的内容)。当然,不排除还存在其他情况会导致此错误,只是笔者还未遇到或听说而已。

五、参考文章:

Connection refused问题的解决:https://blog.csdn.net/wohenfanjian/article/details/51118895
errno:https://www.cnblogs.com/fjutacm/p/5969c7593fdb6516c11a55b0e6813938.html http://www.cnblogs.com/Jimmy1988/p/7485133.html
lsof安装:https://blog.csdn.net/qq_38158631/article/details/78684723
netstat命令安装:https://blog.csdn.net/zpwangshisuifeng/article/details/78526873?locationNum=7&fps=1

你可能感兴趣的:(Linux,网络编程)