socket编程之回声服务器函数的陷阱

简介: 由connect函数使用不当导致的小错误 话不多说先看代码:

由connect函数使用不当导致的小错误

  话不多说先看代码:


server.c


#include<stdio.h>
#include<ctype.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#define SERVER_PORT 9527
int main(void) {
  int fd, cfd, n;
  struct sockaddr_in server_addr, client_addr;
  socklen_t len_c;
  char clie_IP[BUFSIZ];
  char buf[1024];
  bzero(&server_addr, 0);
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(SERVER_PORT);
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  fd = socket(AF_INET, SOCK_STREAM, 0);
  bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
  listen(fd, 25);
  len_c = sizeof(client_addr);
  cfd = accept(fd, (struct sockaddr*)&client_addr, &len_c);
  while (1) {
    n = read(cfd, buf, sizeof(buf));
    for (int i = 0; i < n; i++) {
      buf[i] = toupper(buf[i]);
    }
    write(cfd, buf, n);
    write(STDOUT_FILENO, buf, n);
  }
  close(cfd);
  return 0;
}
client.c
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdio.h>
#include<ctype.h>
#define SERVER_PORT 9527
#define SERVER_IP "127.0.0.1"
int main(void) {
  int sfd, n;
  struct sockaddr_in server_addr;
  char buf[1024];
  sfd = socket(AF_INET, SOCK_STREAM, 0);
  bzero(&server_addr, 0);
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(SERVER_PORT);
  inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
  sfd =connect(sfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
  while (1) { 
    fgets(buf, sizeof(buf), stdin);
    write(sfd, buf, strlen(buf));
    n = read(sfd, buf, sizeof(buf));
    write(STDOUT_FILENO, buf, n);
  }
  close(sfd);
  return 0;
}

最终效果:

client:可以看出来,并没有实现我们的小写转大写功能。

20210423112836511.png

server:

20210423114041387.png

服务器这端直接什么都没有?可以断定可定是出了什么问题了。那么我么继续去排查。


新server.c:主要用于排错


#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#define SERV_PORT 9527
int main(void)
{
    int sfd, cfd;
    int len, i;
    char buf[BUFSIZ], clie_IP[BUFSIZ];
    struct sockaddr_in serv_addr, clie_addr;
    socklen_t clie_addr_len;
    /*创建一个socket 指定IPv4协议族 TCP协议*/
    sfd = socket(AF_INET, SOCK_STREAM, 0);
    /*初始化一个地址结构 man 7 ip 查看对应信息*/
    bzero(&serv_addr, sizeof(serv_addr));           //将整个结构体清零
    serv_addr.sin_family = AF_INET;                 //选择协议族为IPv4
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);  //监听本地所有IP地址
    serv_addr.sin_port = htons(SERV_PORT);          //绑定端口号    
    /*绑定服务器地址结构*/
    bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    /*设定链接上限,注意此处不阻塞*/
    listen(sfd, 64);                                //同一时刻允许向服务器发起链接请求的数量
    printf("wait for client connect ...\n");
    /*获取客户端地址结构大小*/ 
    clie_addr_len = sizeof(clie_addr_len);
    /*参数1是sfd; 参2传出参数, 参3传入传入参数, 全部是client端的参数*/
    cfd = accept(sfd, (struct sockaddr *)&clie_addr, &clie_addr_len);           /*监听客户端链接, 会阻塞*/
//打印出连接成功的客户端的ip和端口号
    printf("client IP:%s\tport:%d\n", 
            inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)), 
            ntohs(clie_addr.sin_port));
    while (1) {
        /*读取客户端发送数据*/
        len = read(cfd, buf, sizeof(buf));
        write(STDOUT_FILENO, buf, len);
        /*处理客户端数据*/
        for (i = 0; i < len; i++)
            buf[i] = toupper(buf[i]);
        /*处理完数据回写给客户端*/
        write(cfd, buf, len); 
    }
    /*关闭链接*/
    close(sfd);
    close(cfd);
    return 0;
}

20210423112905251.png

服务器这端接受的客户端IP为0,也就是根本没有完成和客户端连接,所以客户端得到的是小写也就不足为奇了。


原因剖析:

请大家翻回去看看博主之前写的client.c的代码,write和read可定是没有任何问题的,那么还会有什么地方出问题呢?请仔细思考一下。


我们再来看看 connet函数吧:

截取部分:

20210423114558967.png完整博客:socket编程之 connect()函数

不知道大家是否发现了client.c的问题了?


博主一开始也是着了connect函数的道了,他调用成功之后的返回值居然是0而不是socket描述符。


所以我们可以知道为什么在新编写的server.c中会显示client的ip为0.0.0.0了吧。

20210423114921587.png

就是因为这行代码将本来已经与server连接好的sfd置零了。

删除之后在运行:

client:

20210423115101251.png

20210423115132302.png

所以由此我们需要知道,在做服务器开发的时候我们应该去封装一个专门针对于 bind listen accept connect等函数的出错处理,有了出错处理函数,上述情况的发生是可以大大减小的。


目录
相关文章
|
网络协议 开发者 Python
Socket如何实现客户端和服务器间的通信
通过上述示例,展示了如何使用Python的Socket模块实现基本的客户端和服务器间的通信。Socket提供了一种简单且强大的方式来建立和管理网络连接,适用于各种网络编程应用。理解和掌握Socket编程,可以帮助开发者构建高效、稳定的网络应用程序。
747 10
|
Python
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
使用Python的socket库实现客户端到服务器端的图片传输,包括客户端和服务器端的代码实现,以及传输结果的展示。
1316 3
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
|
JSON 数据格式 Python
Socket学习笔记(一):python通过socket实现客户端到服务器端的文件传输
本文介绍了如何使用Python的socket模块实现客户端到服务器端的文件传输,包括客户端发送文件信息和内容,服务器端接收并保存文件的完整过程。
1176 1
Socket学习笔记(一):python通过socket实现客户端到服务器端的文件传输
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
1202 1
如何在Java中实现多线程的Socket服务器?
在Java中,多线程Socket服务器能同时处理多个客户端连接以提升并发性能。示例代码展示了如何创建此类服务器:监听指定端口,并为每个新连接启动一个`ClientHandler`线程进行通信处理。使用线程池管理这些线程,提高了效率。`ClientHandler`读取客户端消息并响应,支持简单的文本交互,如发送欢迎信息及处理退出命令。
623 2
|
网络协议 数据处理 C语言
利用C语言基于poll实现TCP回声服务器的多路复用模型
此代码仅为示例,展示了如何基于 `poll`实现多路复用的TCP回声服务器的基本框架。在实际应用中,你可能需要对其进行扩展或修改,以满足具体的需求。
441 0
|
10月前
|
弹性计算 运维 安全
阿里云轻量应用服务器与云服务器ECS啥区别?新手帮助教程
阿里云轻量应用服务器适合个人开发者搭建博客、测试环境等低流量场景,操作简单、成本低;ECS适用于企业级高负载业务,功能强大、灵活可扩展。二者在性能、网络、镜像及运维管理上差异显著,用户应根据实际需求选择。
831 10
|
10月前
|
弹性计算 ice
阿里云4核8g服务器多少钱一年?1个月和1小时价格,省钱购买方法分享
阿里云4核8G服务器价格因实例类型而异,经济型e实例约159元/月,计算型c9i约371元/月,按小时计费最低0.45元。实际购买享折扣,1年最高可省至1578元,附主流ECS实例及CPU型号参考。
869 8
|
10月前
|
运维 安全 Ubuntu
阿里云渠道商:服务器操作系统怎么选?
阿里云提供丰富操作系统镜像,涵盖Windows与主流Linux发行版。选型需综合技术兼容性、运维成本、安全稳定等因素。推荐Alibaba Cloud Linux、Ubuntu等用于Web与容器场景,Windows Server支撑.NET应用。建议优先选用LTS版本并进行测试验证,通过标准化镜像管理提升部署效率与一致性。
|
10月前
|
存储 监控 安全
阿里云渠道商:云服务器价格有什么变动?
阿里云带宽与存储费用呈基础资源降价、增值服务差异化趋势。企业应结合业务特点,通过阶梯计价、智能分层、弹性带宽等策略优化成本,借助云监控与预算预警机制,实现高效、可控的云资源管理。