linux——TCP服务器获取客户端IP地址

之前的服务器端代码不灵活,今天对他进行优化

1、优化1:使用INADDR_ANY绑定IP

旧代码

复制代码
sin.sin_addr.s_addr = inet_addr("192.168.88.129");

优化后

复制代码
sin.sin_addr.s_addr = INADDR_ANY;

这个优化的意思

  • 旧写法 :服务器只绑定固定 IP 换一台电脑、换一个网卡、换一个网段 → 连接失败!

  • 新写法 INADDR_ANY服务器绑定本机所有网卡 IP! 不管服务器 IP 是多少不管是哪个网卡不管是内网、外网、虚拟机客户端都能连接!

优点总结

  1. 不挑 IP
  2. 不挑网卡
  3. 代码可移植性极强
  4. 正式项目服务器 100% 这么写

2、优化2:accept 时获取客户端 IP + 端口(正式服务器必备)

旧代码

复制代码
newfd = accept(fd, NULL, NULL);

只拿连接,不关心客户是谁

优化后

复制代码
// 新版
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
newfd = accept(fd,(struct sockaddr *)&cin,&addrlen);

char ipv4_addr[16];
inet_ntop(AF_INET, &cin.sin_addr, ipv4_addr, sizeof(ipv4_addr));

printf("Client:(%s,%d) is connect\n", ipv4_addr, ntohs(cin.sin_port));

复制代码
struct sockaddr_in cin;
  • cin = client in 的意思
  • 作用:准备一个空盒子,用来装客户端的 IP、端口
  • 这个结构体和服务端的 sin 一模一样,只是名字不同

复制代码
socklen_t addrlen = sizeof(cin);
  • addrlen:地址结构体的长度
  • 为什么要写?accept 函数要求必须传一个 "地址长度的指针"
  • 这是固定写法,背下来

复制代码
newfd = accept(fd, (struct sockaddr *)&cin, &addrlen);

旧版后面的两个参数是NULL,优化后会自动的把客户端的IP、端口存到cin里!

复制代码
char ipv4_addr[16];

用来存字符串格式的IP

复制代码
inet_ntop(AF_INET, &cin.sin_addr, ipv4_addr, sizeof(ipv4_addr));

把cin里的二进制IP转成字符串IP

复制代码
printf("Client:(%s,%d) is connect\n",
       ipv4_addr,
       ntohs(cin.sin_port));

打印两个东西:

  1. ipv4_addr → 客户端 IP
  2. ntohs(cin.sin_port) → 客户端端口

为什么用 ntohs?

因为端口在网络里是网络字节序,要转成主机字节序才能正常打印。

ntohs = network to host short

这个优化的意思

旧版:

只知道有人连接了,但不知道是谁连的

新版:

accept 时自动把客户端的 IP 地址、客户端端口号取出来,然后打印

这个优化的 3 个好处

  1. 可以知道谁连接了服务器

  2. 方便日志记录

  3. 正式网络程序必须这么写

    #include<stdio.h>
    #include <sys/types.h> /* See NOTES */
    #include <sys/socket.h>
    #include <unistd.h>
    #include<stdlib.h>
    #include <strings.h>
    #include <arpa/inet.h>
    #include<string.h>

    #define QUIT_STR "QUIT"
    #define BUFSIZE 1024
    #define BACKLOG 5
    #define SERV_IP 5001
    #define SERV_IP_ADDR "192.168.88.129"
    int main()
    {
    int fd = -1;

    复制代码
     struct sockaddr_in sin;
     //1.socket
     fd = socket(AF_INET,SOCK_STREAM,0);
     if(fd<0)
     {
     	perror("socket");
     	exit(1);
     }
    
     bzero(&sin,sizeof(sin));
    
     sin.sin_family = AF_INET;
     sin.sin_port = htons(SERV_IP);
     //sin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);  
     sin.sin_addr.s_addr = INADDR_ANY;
     /*if(inet_pion(AF_INET,SERV_IP_ADDR,(void *)sin.sin_addr.s_addr) != 1)
       {
       perror("inet_pton");
       exit(1);
       }
       */
     //2.bind
     if(bind(fd,(struct sockaddr *)&sin,sizeof(sin)) <0)
     {
     	perror("bind");
     	exit(0);
     }
     //3.listen
     if(listen(fd,BACKLOG) < 0)
     {
     	perror("listen");
     	exit(1);
     }
     
     //4.accept
     /*int newfd = -1;
     newfd = accept(fd,NULL,NULL);
     if(newfd < 0)
     {
     	perror("accept");
     	exit(1);
     }
     */
     int newfd = -1;
     struct sockaddr_in cin;
     socklen_t addrlen = sizeof(cin);
     newfd = accept(fd,(struct sockaddr *)&cin,&addrlen);
     if(newfd < 0)
     {
     	perror("accept");
     	exit(1);
     }
    
     char ipv4_addr[16];
     if(!inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
     {
     	perror("inet_ntop");
     	exit(1);
     }
     printf("Client:(%s,%d) is connect\n",ipv4_addr,ntohs(cin.sin_port));
    
     char buf[BUFSIZE];
     int ret = -1;
     //read
     while(1)
     {
     	do
     	{
     	bzero(buf,BUFSIZE);
     	ret = read(newfd,buf,BUFSIZE-1);
     	}while(ret < 1);
     	if(ret < 0)
     	{
     		exit(1);
     	}
     	if(!ret)
     	{
     		break;
     	}
     	printf("receive data:%s\n",buf);
    
     	if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))
     	{
     		printf("Client is exiting!\n");
     		break;
     	}
     }
     
     close(newfd);
     close(fd);
    
     return 0;

    }

相关推荐
CDN3602 小时前
高防服务器磁盘 / CPU 爆满?攻击引流与资源扩容实战
运维·服务器·网络协议
小贾要学习2 小时前
【Linux】应用层自定义协议与序列化
linux·服务器·c++·json
white-persist2 小时前
【vulhub spring CVE-2018-1270】CVE-2018-1270 Spring Messaging 远程命令执行漏洞 完整复现详细分析解释
java·服务器·网络·数据库·后端·python·spring
Amnesia0_02 小时前
理解Linux中的OS管理和进程属性
linux·运维·服务器
十五年专注C++开发2 小时前
cpolar(极点云): 一款主流的内网穿透工具
linux·windows·cpolar·穿透
徒 花2 小时前
HCIP学习05 链路聚合(Eth-Trunk)+ VRRP
服务器·网络·学习·hcip
liliangcsdn2 小时前
LLM如何与mcp server交互示例
linux·开发语言·python
小夏子_riotous2 小时前
openstack的使用——7. 共享文件系统manila服务
linux·运维·服务器·系统架构·centos·openstack·运维开发
南境十里·墨染春水2 小时前
linux学习进展 进程的内存管理
linux·服务器·学习