【Linux】socket编程1-服务端代码

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:题目解析

在这里插入图片描述


👉🏻socket网络编程常用头文件

在C++中使用socket,你需要包含以下头文件:

  1. <sys/socket.h>:这个头文件包含了使用socket所需的系统级别的函数和数据结构的声明。

  2. <netinet/in.h>:该头文件包含了定义网络地址结构 struct sockaddr_in 的声明,以及一些网络相关的常量和函数原型。

  3. <arpa/inet.h>:这个头文件包含了一些与网络地址转换相关的函数原型,例如 inet_addr()inet_ntoa()

  4. <unistd.h>:这个头文件包含了一些系统调用函数的原型,例如 close(),exit()

在 Windows 平台上,你可能需要包含不同的头文件,例如 <winsock2.h><ws2tcpip.h>

👉🏻socket网络编程预备知识函数

bzero

bzero 函数在标准C库中已经被弃用,它通常用于将一块内存区域清零。在现代C++编程中,推荐使用更安全和更具可移植性的替代方案,如 memset 函数或者使用 C++ 标准库提供的 std::fill 函数。

然而,如果你仍然需要了解 bzero 函数的工作原理,我可以简要介绍一下。

bzero 函数通常的形式是这样的:

void bzero(void *s, size_t n);

它接受两个参数:

  • s:指向要清零的内存区域的指针。
  • n:要清零的字节数。

bzero 函数的作用是将从 s 开始的连续 n 个字节设置为零。它通常用于初始化或清空某些内存区域,例如清空一个字符数组或一个结构体。

以下是一个示例用法:

#include <stdio.h>
#include <strings.h>

int main() {
    char buffer[10];
    bzero(buffer, sizeof(buffer));  // 将 buffer 中的所有字节清零

    // 在清零后,buffer 中的内容应该全是 0
    for (int i = 0; i < sizeof(buffer); ++i) {
        printf("%d ", buffer[i]);
    }

    return 0;
}

然而,需要注意的是,bzero 函数已经被标记为废弃,因为它不够安全。在一些情况下,编译器可能会发出警告,建议使用更安全的替代方案。

inet_addr

inet_addr 函数是一个用于将点分十进制的 IPv4 地址转换为网络字节顺序的 32 位二进制整数的函数。它在网络编程中经常用于将 IP 地址转换为网络套接字地址结构中使用的格式。

该函数的原型如下:

#include <arpa/inet.h>

in_addr_t inet_addr(const char *cp);

它接受一个指向以空字符结尾的字符串的指针作为参数,并返回一个 in_addr_t 类型的值,该值表示转换后的 IP 地址。

下面是一个示例用法:

#include <stdio.h>
#include <arpa/inet.h>

int main() {
    const char *ip_address_str = "192.168.1.1";
    in_addr_t ip_address = inet_addr(ip_address_str);

    if (ip_address == INADDR_NONE) {
        printf("Invalid IP address\n");
    } else {
        printf("IP address in network byte order: %u\n", ip_address);
    }

    return 0;
}

需要注意的是,inet_addr 函数存在一些限制和问题,例如不能处理 IPv4 子网掩码,而且在处理不合法的 IP 地址时可能会返回 INADDR_NONE。因此,更安全和更强大的替代方案是使用 inet_pton 函数,它能够处理更多的情况并提供更好的错误处理机制。

recvfrom

recvfrom 函数是在进行网络编程时常用的一个函数,它用于从一个已连接的套接字接收数据,或者从未连接的套接字接收数据和地址。具体来说,recvfrom 通常用于在 UDP 套接字中接收数据,因为 UDP 是面向消息的协议,不像 TCP 那样有连接的概念。

该函数的原型如下:

#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);

它接受以下参数:

  • sockfd:套接字文件描述符,指定要接收数据的套接字。
  • buf:接收数据的缓冲区的指针。
  • len:缓冲区的大小。
  • flags:接收操作的标志,通常设置为 0。
  • src_addr:一个指向 sockaddr 结构的指针,用于存储发送方的地址信息。
  • addrlen:一个指向 socklen_t 类型变量的指针,用于存储发送方地址信息的长度。

recvfrom 函数的返回值是接收到的数据的字节数。如果返回值为 -1,则表示出现了错误。

下面是一个简单的示例用法,演示了如何使用 recvfrom 函数接收 UDP 数据:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sockfd;
    char buffer[BUFFER_SIZE];
    struct sockaddr_in servaddr, cliaddr;
    socklen_t len = sizeof(cliaddr);

    // 创建UDP套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 绑定地址和端口
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(PORT);

    if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    // 接收数据
    ssize_t n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, 0,
                         (struct sockaddr *)&cliaddr, &len);
    buffer[n] = '\0'; // 在接收到的数据后面添加字符串结束符

    printf("Received message: %s\n", buffer);

    close(sockfd);
    return 0;
}

这个示例创建了一个UDP套接字,并绑定到指定的端口。然后使用 recvfrom 函数接收来自客户端的数据,并将数据打印出来。

ssize_t 是一个有符号整数类型,用于表示某些系统调用(如 readwriterecvfrom 等)的返回值或错误码。它通常被定义为 typedef,在标准库中可以找到类似以下的定义:

typedef long ssize_t;

ssize_t 的长度通常与指针的长度相同,即在 32 位系统上为 4 字节,在 64 位系统上为 8 字节。因为它是有符号类型,所以可以表示负数,通常用于表示字节数或者错误码,以便能够返回错误的情况。

在标准的 POSIX 环境中,ssize_t 的定义在 sys/types.h 头文件中。

sendto

sendto 函数用于通过已连接或未连接的套接字发送数据到指定的目标地址。通常在网络编程中,它被用于在 UDP 套接字中发送数据,因为 UDP 是面向消息的协议,不像 TCP 那样有连接的概念。

该函数的原型如下:

#include <sys/socket.h>

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);

它接受以下参数:

  • sockfd:套接字文件描述符,指定要发送数据的套接字。
  • buf:要发送数据的缓冲区的指针。
  • len:要发送数据的大小。
  • flags:发送操作的标志,通常设置为 0。
  • dest_addr:一个指向 sockaddr 结构的指针,用于指定目标地址。
  • addrlen:目标地址信息的长度。

sendto 函数的返回值是实际发送的数据的字节数。如果返回值为 -1,则表示出现了错误。

下面是一个简单的示例用法,演示了如何使用 sendto 函数发送 UDP 数据:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>

#define PORT 8080
#define BUFFER_SIZE 1024

int main() {
    int sockfd;
    char buffer[BUFFER_SIZE];
    struct sockaddr_in servaddr;

    // 创建UDP套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址和端口
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(PORT);

    // 发送数据
    const char *message = "Hello, UDP Server!";
    ssize_t n = sendto(sockfd, message, strlen(message), 0,
                       (const struct sockaddr *)&servaddr, sizeof(servaddr));
    if (n < 0) {
        perror("sendto failed");
        exit(EXIT_FAILURE);
    }

    printf("Message sent successfully.\n");

    close(sockfd);
    return 0;
}

这个示例创建了一个UDP套接字,并设置了服务器的地址和端口。然后使用 sendto 函数向服务器发送数据。

👉🏻正式开搞——编写服务端代码

Makefile(生成目标文件)

Udpserver:Main.cc
	g++ -o $@ $^ -std=c++14
.PHONY:clean
clean:
	rm -rf Udpserver

Log.hpp(用来打印日志信息)

#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;
enum
{
    Debug = 0,
    Info,
    Warning,
    Error,
    Fatal
};

enum
{
    Screen = 10,
    OneFile,
    ClassFile
};

std::string LevelToString(int level)
{
    switch (level)
    {
    case Debug:
        return "Debug";
    case Info:
        return "Info";
    case Warning:
        return "Warning";
    case Error:
        return "Error";
    case Fatal:
        return "Fatal";
    default:
        return "Unknown";
    }
}

const int defaultstyle = Screen;
const std::string default_filename = "log.";
const std::string logdir = "log";

class Log
{
public:
    Log() : style(defaultstyle), filename(default_filename)
    {
        mkdir(logdir.c_str(), 0775);
    }
    void Enable(int sty) //
    {
        style = sty;
    }
    std::string TimeStampExLocalTime()
    {
        time_t currtime = time(nullptr);
        struct tm* curr = localtime(&currtime);
        char time_buffer[128];
        snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",
            curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday,
            curr->tm_hour, curr->tm_min, curr->tm_sec);
        return time_buffer;
    }
    void WriteLogToOneFile(const std::string& logname, const std::string& message)
    {
        umask(0);
        int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
        if (fd < 0) return;
        write(fd, message.c_str(), message.size());
        close(fd);
        // std::ofstream out(logname);
        // if (!out.is_open())
        //     return;
        // out.write(message.c_str(), message.size());
        // out.close();
    }
    void WriteLogToClassFile(const std::string& levelstr, const std::string& message)//将内容写入某个文件中
    {
        std::string logname = logdir;
        logname += "/";
        logname += filename;
        logname += levelstr;
        WriteLogToOneFile(logname, message);
    }

    void WriteLog(const std::string& levelstr, const std::string& message)//决定日志写入的位置:屏幕/单个文件/多个文件
    {
        switch (style)
        {
        case Screen:
            std::cout << message;
            break;
        case OneFile:
            WriteLogToClassFile("all", message);//单个文件
            break;
        case ClassFile:
            WriteLogToClassFile(levelstr, message);//多个文件
            break;
        default:
            break;
        }
    }
    void LogMessage(int level, const char* format, ...) // 类C的一个日志接口
    {
        char leftbuffer[1024];
        std::string levelstr = LevelToString(level);
        std::string currtime = TimeStampExLocalTime();
        std::string idstr = std::to_string(getpid());

        char rightbuffer[1024];
        va_list args; // char *, void *
        va_start(args, format);
        // args 指向了可变参数部分
        vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);
        va_end(args); // args = nullptr;
        snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s] ",
            levelstr.c_str(), currtime.c_str(), idstr.c_str());

        std::string loginfo = leftbuffer;
        loginfo += rightbuffer;
        WriteLog(levelstr, loginfo);
    }
    // void operator()(int level, const char *format, ...)
    // {
    //     LogMessage(int level, const char *format, ...)
    // }
    ~Log() {}

private:
    int style;
    std::string filename;
};

Log lg;

class Conf
{
public:
    Conf()
    {
        lg.Enable(Screen);
    }
    ~Conf()
    {}
};

Conf conf;

ComErr.hpp(放置错误码)

#pragma once

enum
{
    Usage_Err = 1,
    Socket_Err,
    Bind_Err
};

Main.cc

#include "Udpserver.hpp"
#include "ComErr.hpp"
#include <memory>

void Usage(std::string proc)
{
    std::cout << "Usage : \n\t" << proc << " local_ip local_port\n" << std::endl;
}
int main(int argc,char* argv[3])
{
    if(argc!=3)//如果命令行指令数没有三个
    {
        Usage(argv[0]);
        return Usage_Err;
    }
    string ip = argv[1];//获取ip
    uint16_t port = stoi(argv[2]);//获取端口号

    //接下来创建Udpserver的指针

    unique_ptr<Udpsever> usvr = std::make_unique<Udpsever>(ip,port);//unique_ptr不支持拷贝构造和赋值操作,因此确保了资源的独占性
    //使用make_unique记得添加memory头文件
    usvr->Init();
    usvr->Start();

    return 0;
}

UdpServer.hpp(服务端代码)

#include<iostream>
#include "ComErr.hpp"
#include"Log.hpp"
#include <cstring>
#include<sys/types.h>
#include<sys/socket.h>
#include<cerrno>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>

using namespace std;

const string defaultip = "127.0.0.0";
const uint16_t defaultport = 8888;
class Udpsever
{
public:
    Udpsever(Udpsever& udp)=delete;//防止拷贝
    Udpsever(const Udpsever& udp)=delete;
    const Udpsever& operator=(Udpsever& udp)=delete;

    Udpsever(const string& ip = defaultip,const uint16_t& port = defaultport)
    :_ip(ip),_port(port)
    {}

    void Init()
    {
        //1.创建套接字对象
        _sockfd = socket(AF_INET,SOCK_DGRAM,0);//创建一个套接字对象,协议族为IPv4,套接字类型是数据报套接字
        if(_sockfd<0)
        {
            //创建失败则打印错误日志
            lg.LogMessage(Fatal,"socket err,%d : %s\n",errno,strerror(errno));//strerror需要包含头文件cstring
            exit(Socket_Err);
        }
        //成功则打印返回的套接字描述符
        lg.LogMessage(Info,"sockfd : %d\n",_sockfd);

        //上述我们只是创建了一个套接字对象,但是我们还必须将套接字对象与特定的网络信息(ip地址+端口号)绑定,这样以后其它计算机才能根据这个网络信息找到我们然后进行通信
        //所以接下来我们要与网络信息绑定,才能实现在网络中进行通信
        //我们要用到socket编程中的bind函数:用于将套接字与特定的地址(IP地址和端口号)绑定在一起,以便其他计算机可以找到并与之通信
        //2.bind并指定网络信息
        //2.1指定网络地址信息
        
        struct sockaddr_in local;//创建一个网络地址信息结构体,接下里初始化地址信息
        bzero(&local,sizeof(local));//相当于memset
        local.sin_family = AF_INET;//1.初始化协议族
        local.sin_port = htons(_port);//2.将16位无符号短整数从主机字节序转换为网络字节序(大端序)
        local.sin_addr.s_addr = inet_addr(_ip.c_str());//3.初始化ip地址,这里我们想让_ip变为4字节且是网络字节序,所以使用转换函数inet_addr

        //2.2将网络地址信息与socket文件对象进行绑定
        //bind函数原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
        int bd = bind(_sockfd,(const struct sockaddr *)&local,sizeof(local));
        if(bd!=0)
        {
            lg.LogMessage(Fatal,"bind err :%d %s\n",errno,strerror(errno));
            exit(Bind_Err);
        }
        //至此socket初始化完成

        

    }
    void Start()
    {
        int defaultbuffer = 1024;
        char buffer[defaultbuffer];
        while(true)//服务器永远不退出
        {
            //接受信息
            struct sockaddr_in peer;//用来存储发送方的地址信息
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd,buffer,sizeof(buffer)-1/*再减1是给\0留位置*/,0,(struct sockaddr*)&peer,&len);
            if(n!=-1)
            {
                //说明接受信息成功
                //接下来我们将打印发送方的信息
                buffer[n] = '\0';
                cout<<"Client say : "<<buffer<<endl;
                //接下来也发送一些信息回应客户端
                char retstr[] = "Got it,processing...";
                sendto(_sockfd,retstr,sizeof(retstr),0,(struct sockaddr*)&peer,len);
            }
        }
    }
private:
    string _ip;//ip地址
    uint16_t _port;//端口号
    int _sockfd;//套接字描述符
};

代码写完后,我们运行完成后。
再用netstat命令查看服务器状态,看看是否生成了我们的服务端服务器
在这里插入图片描述
可以看到确实出现了。

netstat

netstat 命令用于显示网络状态信息,包括网络连接、路由表、接口统计信息等。在 Linux 系统中,netstat 命令通常用于查看网络连接的状态、路由表、接口信息等,以便网络故障排除、性能监控等任务。

netstat 命令的基本语法如下:

netstat [options]

常用的选项包括:

  • -a:显示所有连接和监听端口。
  • -t:仅显示 TCP 协议的连接。
  • -u:仅显示 UDP 协议的连接。
  • -n:以数字形式显示地址和端口号,而不是以主机名和服务名显示。
  • -p:显示建立连接的进程标识符 PID 和程序名称。
  • -r:显示路由表信息。
  • -i:显示网络接口信息。

以下是一些常用的示例用法:

  1. 显示所有连接和监听端口:
netstat -a
  1. 仅显示 TCP 连接:
netstat -t
  1. 仅显示 UDP 连接:
netstat -u
  1. 以数字形式显示所有连接和监听端口:
netstat -an
  1. 显示建立连接的进程标识符 PID 和程序名称:
netstat -p
  1. 显示路由表信息:
netstat -r
  1. 显示网络接口信息:
netstat -i

除了上述选项之外,还有其他一些选项和参数可供选择,可以通过 man netstat 命令查看 netstat 命令的详细使用手册。

  • 22
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一段 Linux 下使用 C 语言实现 socket 编程的示例代码(客户端): ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8888 #define MAXDATASIZE 100 int main(int argc, char *argv[]) { int sockfd, num; char buf[MAXDATASIZE]; struct sockaddr_in server; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket() error."); exit(1); } server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr.s_addr = inet_addr("127.0.0.1"); bzero(&(server.sin_zero), 8); if (connect(sockfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { perror("connect() error."); exit(1); } if ((num = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) { perror("recv() error."); exit(1); } buf[num - 1] = '\0'; printf("Server Message: %s\n", buf); close(sockfd); return 0; } ``` 这是一段 Linux 下使用 C 语言实现 socket 编程的示例代码服务器端): ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8888 #define BACKLOG 1 int main() { int sockfd, new_fd; struct sockaddr_in server; struct sockaddr_in client; int sin_size; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket() error."); exit(1); } int opt = SO_REUSEADDR; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr ### 回答2: 下面是一个使用C语言编写的在Linux下使用Socket编程实现客户端和服务端对话的示例代码服务端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define MAX_BUFFER_SIZE 1024 int main() { int server_socket, client_socket; struct sockaddr_in server_address, client_address; char buffer[MAX_BUFFER_SIZE]; int str_len; socklen_t client_address_size; server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket == -1) { perror("socket() error"); exit(1); } memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_port = htons(1234); server_address.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) { perror("bind() error"); exit(1); } if (listen(server_socket, 5) == -1) { perror("listen() error"); exit(1); } client_address_size = sizeof(client_address); client_socket = accept(server_socket, (struct sockaddr*)&client_address, &client_address_size); if (client_socket == -1) { perror("accept() error"); exit(1); } while (1) { str_len = read(client_socket, buffer, MAX_BUFFER_SIZE - 1); if (str_len == -1) { perror("read() error"); exit(1); } buffer[str_len] = '\0'; printf("Client: %s\n", buffer); printf("Server: "); fgets(buffer, MAX_BUFFER_SIZE, stdin); if (!strcmp(buffer, "q\n") || !strcmp(buffer, "Q\n")) { break; } write(client_socket, buffer, strlen(buffer)); } close(client_socket); close(server_socket); return 0; } ``` 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define MAX_BUFFER_SIZE 1024 int main() { int client_socket; struct sockaddr_in server_address; char buffer[MAX_BUFFER_SIZE]; int str_len; client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket == -1) { perror("socket() error"); exit(1); } memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_port = htons(1234); server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(client_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) { perror("connect() error"); exit(1); } while (1) { printf("Client: "); fgets(buffer, MAX_BUFFER_SIZE, stdin); if (!strcmp(buffer, "q\n") || !strcmp(buffer, "Q\n")) { break; } write(client_socket, buffer, strlen(buffer)); str_len = read(client_socket, buffer, MAX_BUFFER_SIZE - 1); if (str_len == -1) { perror("read() error"); exit(1); } buffer[str_len] = '\0'; printf("Server: %s\n", buffer); } close(client_socket); return 0; } ``` 以上是一个简单的客户端和服务端对话的例子,运行服务端程序后,再运行客户端程序就可以实现在终端上进行对话。客户端输入的内容会发送给服务端,在服务端的终端显示,并等待服务端的回复,服务端在接收到客户端的消息后会回复给客户端,并在客户端的终端显示。当输入"q"或"Q"时,客户端和服务端会断开连接并退出程序。 ### 回答3: 下面是一段使用C语言在Linux下实现客户端与服务端对话的代码: 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #define BUF_SIZE 1024 int main() { int client_socket; struct sockaddr_in server_address; char buffer[BUF_SIZE]; // 创建套接字 client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket == -1) { perror("创建套接字失败"); exit(1); } // 设置服务器地址 memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器IP地址 server_address.sin_port = htons(8888); // 服务器端口号 // 连接服务器 if (connect(client_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) { perror("连接服务器失败"); exit(1); } while (1) { // 输入要发送的消息 printf("请输入要发送的消息(输入exit退出):"); fgets(buffer, BUF_SIZE, stdin); // 发送消息给服务器 if (send(client_socket, buffer, strlen(buffer), 0) == -1) { perror("发送消息给服务器失败"); exit(1); } // 退出循环的条件 if (strcmp(buffer, "exit\n") == 0) { break; } // 接收服务器返回的消息 memset(buffer, 0, BUF_SIZE); if (recv(client_socket, buffer, BUF_SIZE - 1, 0) == -1) { perror("接收服务器消息失败"); exit(1); } // 打印服务器返回的消息 printf("服务器消息:%s", buffer); } // 关闭套接字 close(client_socket); return 0; } ``` 服务端代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #define BUF_SIZE 1024 int main() { int server_socket, client_socket; struct sockaddr_in server_address, client_address; socklen_t client_address_size; char buffer[BUF_SIZE]; // 创建套接字 server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket == -1) { perror("创建套接字失败"); exit(1); } // 设置服务器地址 memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_ANY); // 任意IP地址 server_address.sin_port = htons(8888); // 服务器监听端口号 // 绑定套接字 if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) { perror("绑定套接字失败"); exit(1); } // 监听套接字 if (listen(server_socket, 5) == -1) { perror("监听套接字失败"); exit(1); } printf("等待客户端连接...\n"); // 接受客户端连接 client_address_size = sizeof(client_address); client_socket = accept(server_socket, (struct sockaddr*)&client_address, &client_address_size); if (client_socket == -1) { perror("接受客户端连接失败"); exit(1); } printf("客户端连接成功\n"); while (1) { // 接收客户端发送的消息 memset(buffer, 0, BUF_SIZE); if (recv(client_socket, buffer, BUF_SIZE - 1, 0) == -1) { perror("接收客户端消息失败"); exit(1); } // 退出循环的条件 if (strcmp(buffer, "exit\n") == 0) { break; } // 打印客户端发送的消息 printf("客户端消息:%s", buffer); // 回复客户端消息 if (send(client_socket, buffer, strlen(buffer), 0) == -1) { perror("回复客户端消息失败"); exit(1); } } // 关闭套接字 close(client_socket); close(server_socket); return 0; } ``` 这段代码实现了一个简单的客户端和服务端对话的功能。客户端使用`socket`函数创建了一个套接字,然后通过`connect`函数连接到服务器上。连接成功后,进入一个循环,循环中首先通过`fgets`函数输入要发送的消息,然后通过`send`函数将消息发送给服务器。接着接收服务器返回的消息,再打印出来。如果输入的消息是"exit",则退出循环,关闭套接字,程序结束。 服务端使用`socket`函数创建一个套接字,并设置服务器地址。然后通过`bind`函数将套接字与服务器地址绑定,并通过`listen`函数监听套接字。接着使用`accept`函数接受客户端连接,接收成功后进入一个循环。循环中首先接收客户端发送的消息,然后通过`send`函数将消息回复给客户端。如果接收到的消息是"exit",则退出循环,关闭套接字,程序结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值