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;

    }

相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux