linux套接字

一. 套接字基础

1.1、 什么是套接字

套接字(Socket)是一种用于在计算机之间进行通信的软件组件或接口。它提供了一种标准化的方式,使得应用程序能够通过网络连接进行相互之间的通信。

在计算机网络中,每台计算机都有一个唯一的IP地址,用于标识这台计算机。当应用程序需要在网络上进行通信时,它们可以使用套接字来建立连接并进行数据交换。套接字可以使用不同的传输协议,例如TCP和UDP,以实现不同的通信需求。

套接字有两种类型:客户端套接字和服务器套接字。客户端套接字通常用于向服务器请求服务,而服务器套接字则用于接受客户端的请求并提供服务。套接字也可以通过网络套接字(网络套接字)进行通信,这种套接字允许不同计算机之间的通信。

1.2、 套接字类型

在套接字编程中,通常有两种类型的套接字:流套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)

  1. 流套接字(SOCK_STREAM):流套接字是一种面向连接的套接字类型,提供可靠的、有序的、基于字节流的数据传输服务。使用流套接字时,应用程序可以像读写文件一样,通过连接进行数据的传输。这种套接字通常用于需要可靠性和顺序性的数据传输场景,如HTTP、SMTP等协议。

  2. 数据报套接字(SOCK_DGRAM):数据报套接字是一种无连接的套接字类型,提供不可靠的、无序的、固定长度的数据传输服务。使用数据报套接字时,应用程序需要明确指定目的地址和端口号,每次传输的数据长度也必须小于套接字的缓冲区大小。这种套接字通常用于需要高效率的数据传输场景,如DNS、TFTP等协议。

除了这两种基本类型外,还有其他类型的套接字,如原始套接字(SOCK_RAW)、信号套接字(SOCK_SEQPACKET)等。这些套接字类型都具有不同的特点和用途,应根据具体情况选择适合的套接字类型。

1.3、 套接字地址

在套接字编程中,套接字地址是一个结构体,用于表示套接字的通信地址和端口号。在不同的套接字类型和协议中,套接字地址的结构和属性可能会有所不同。

下面是一个常见的套接字地址结构体sockaddr_in的定义:

struct sockaddr_in {
    sa_family_t sin_family; // 地址族(Address Family)
    in_port_t sin_port; // 端口号
    struct in_addr sin_addr; // IP地址
    char sin_zero[8]; // 未使用
};

其中,sin_family字段表示地址族,通常是AF_INET表示IPv4,sin_port字段表示端口号,sin_addr字段表示IP地址。套接字地址结构体的大小通常为16个字节,使用sockaddr_in类型的指针可以将其作为参数传递给套接字函数。

在套接字编程中,通常使用套接字地址来指定套接字的通信目标,例如在创建套接字时指定本地IP地址和端口号,在发送数据时指定远程IP地址和端口号等。在不同的套接字类型和协议中,套接字地址的使用方法和约定可能会有所不同,应根据具体情况进行选择。

1.4、 套接字传输方式

在套接字编程中,套接字可以采用不同的传输方式进行数据传输,常见的传输方式包括:

  1. 阻塞式传输:在阻塞式传输模式下,套接字函数调用会一直阻塞,直到数据传输完成或者出现错误。在阻塞式传输模式下,应用程序需要等待套接字函数返回才能继续执行其他任务,因此不适用于需要同时处理多个套接字的场景。

  2. 非阻塞式传输:在非阻塞式传输模式下,套接字函数调用会立即返回,不会阻塞应用程序的执行。如果数据传输没有完成,套接字函数可能会返回一个错误码,应用程序可以通过轮询或者异步IO等方式来检查传输状态,直到数据传输完成。在非阻塞式传输模式下,应用程序需要额外的代码来处理传输状态的检查和错误处理等任务。

  3. 多路复用传输:多路复用传输指的是利用select、poll、epoll等机制来同时处理多个套接字的传输。在多路复用传输模式下,应用程序可以通过一个线程或者进程来同时处理多个套接字的数据传输,提高了应用程序的并发性能。

需要注意的是,在不同的传输方式下,套接字函数的调用方式和返回值可能会有所不同,应根据具体情况选择合适的传输方式。

为了实现网络程序的可移植性,需要确保在不同的计算机体系结构下,数据的字节顺序都是一致的。在网络编程中,通常使用网络字节序(也称为大端字节序)来传输数据。而主机字节序则取决于计算机的体系结构。

为了将主机字节序转换为网络字节序,可以使用以下库函数

  1. htons():将16位主机字节序转换为网络字节序(大端字节序)。
  2. htonl():将32位主机字节序转换为网络字节序(大端字节序)。
  3. ntohs():将16位网络字节序转换为主机字节序。
  4. ntohl():将32位网络字节序转换为主机字节序。

这些函数通常被定义在<arpa/inet.h>头文件中,可以通过在程序中包含该头文件来调用这些函数。在发送数据时,使用htons()和htonl()函数将主机字节序转换为网络字节序;在接收数据时,使用ntohs()和ntohl()函数将网络字节序转换为主机字节序,以确保数据正确解析。

二. Linux套接字编程基础

2.1、 创建套接字        (tcp,udp使用)

在Linux套接字编程中,创建套接字需要使用socket()系统调用。socket()函数会返回一个整型的文件描述符,该文件描述符可以用于后续的套接字操作。

socket()函数的原型如下:

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

参数说明:

  • domain:指定套接字的协议族,可以是AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNIX(UNIX域套接字)等。
  • type:指定套接字的类型,可以是SOCK_STREAM(流式套接字,用于TCP协议)、SOCK_DGRAM(数据报套接字,用于UDP协议)等。
  • protocol:指定协议类型,可以是IPPROTO_TCP(TCP协议)、IPPROTO_UDP(UDP协议)等。通常设置为0,让系统自动选择合适的协议。
  • 返回值

下面是一个简单的C语言例子,演示了如何创建一个TCP套接字:

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

int main(int argc, char **argv) {
  int sockfd;

  // 创建套接字
  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) {    //创建失败返回-1
    perror("socket failed");
    exit(EXIT_FAILURE);
  }

  // 其他操作...

  return 0;
}

在这个例子中,使用socket()函数创建了一个TCP套接字,并将其赋值给sockfd变量。如果创建套接字失败,会使用perror()函数打印错误信息并退出程序。

需要注意的是,socket()函数返回的文件描述符并不是真正的套接字,它只是对内核中的一个套接字结构体的引用,用于后续的套接字操作。如果需要关闭套接字,可以使用close()函数关闭该文件描述符。

2.2、 绑定套接字        (tcp,udp使用)

在使用Linux套接字编程时,创建套接字之后需要将套接字与一个特定的网络地址和端口号绑定(bind)起来,这样才能够通过该地址和端口号访问该套接字。

client 需不需要bind??? 需要bind,但是不需要用户自己bind,而是os自动给你bind

所谓的"不需要",指的是: 不需要用户自己bind端口信息!因为OS会自动给你绑定,你也最好这么做!

在C语言中,使用bind()函数将套接字与指定的网络地址和端口号绑定。bind()函数的原型如下:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

  • sockfd:要绑定的套接字文件描述符。
  • addr:指向用于绑定的sockaddr结构体变量的指针,包含了网络地址和端口号等信息。

不同的网络协议的地址格式并不相同,我们可以通过强转成struct sockaddr确定同一个类型

  • addrlen:sockaddr结构体的长度。
  • 返回值

下面是一个简单的例子,演示了如何创建一个TCP套接字,并将其绑定到本机的8080端口:

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

int main(int argc, char **argv) {
  int sockfd;
  struct sockaddr_in addr;

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

  // 绑定套接字
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(8080);
  if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
  }

  // 其他操作...

  return 0;
}

在这个例子中,首先使用socket()函数创建了一个TCP套接字,并将其赋值给sockfd变量。然后使用memset()函数将addr变量清零,并设置addr结构体的相关参数,包括地址族、IP地址、端口号等。最后调用bind()函数将套接字绑定到指定的地址和端口号。如果绑定失败,会使用perror()函数打印错误信息并退出程序。

需要注意的是,绑定的地址和端口号必须是未被其他套接字占用的,否则绑定失败。此外,如果要将套接字绑定到特定的IP地址,需要将addr.sin_addr.s_addr设置为该IP地址的网络字节序表示。如果想要将套接字绑定到所有可用的网络接口,可以将addr.sin_addr.s_addr设置为INADDR_ANY。

2.3、 监听套接字        (tcp使用)

在使用Linux套接字编程时,创建并绑定套接字之后,需要将套接字转换为被动模式,并开始监听(listen)来自客户端的连接请求。

在C语言中,使用listen()函数将套接字转换为被动模式,并开始监听来自客户端的连接请求。listen()函数的原型如下:

#include <sys/socket.h>

int listen(int sockfd, int backlog);

参数说明:

  • sockfd:要监听的套接字文件描述符。
  • backlog:等待连接队列的最大长度。、
  • 返回值

下面是一个简单的例子,演示了如何创建一个TCP套接字,并将其绑定到本机的8080端口,然后将套接字转换为被动模式,并开始监听来自客户端的连接请求:

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

int main(int argc, char **argv) {
  int sockfd;
  struct sockaddr_in addr;

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

  // 绑定套接字
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(8080);
  if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
  }

  // 监听套接字
  if (listen(sockfd, 5) < 0) {
    perror("listen failed");
    exit(EXIT_FAILURE);
  }

  // 其他操作...

  return 0;
}

在这个例子中,首先使用socket()函数创建了一个TCP套接字,并将其赋值给sockfd变量。然后使用memset()函数将addr变量清零,并设置addr结构体的相关参数,包括地址族、IP地址、端口号等。接着使用bind()函数将套接字绑定到指定的地址和端口号。最后调用listen()函数将套接字转换为被动模式,并开始监听来自客户端的连接请求。如果监听失败,会使用perror()函数打印错误信息并退出程序。

需要注意的是,backlog参数指定了等待连接队列的最大长度。当客户端连接请求到达时,如果等待连接队列已满,新的连接请求将被拒绝。在Linux系统中,等待连接队列的长度通常限制在数百到数千之间。如果需要处理大量的并发连接请求,可以使用多线程或多进程等技术来提高性能。

2.4、连接套接字        (tcp使用)

在使用Linux套接字编程时,客户端需要通过connect()函数与服务端建立连接。connect()函数的原型如下:

#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

  • sockfd:要连接的套接字文件描述符。
  • addr:指向服务端地址的指针。
  • addrlen:服务端地址的长度。
  • 返回值

 

下面是一个简单的例子,演示了如何创建一个TCP客户端,连接到服务端的IP地址为192.168.0.2、端口号为8080的套接字上:

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

int main(int argc, char **argv) {
  int sockfd;
  struct sockaddr_in addr;

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

  // 连接套接字
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = inet_addr("192.168.0.2");
  addr.sin_port = htons(8080);
  if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    perror("connect failed");
    exit(EXIT_FAILURE);
  }

  // 其他操作...

  return 0;
}

在这个例子中,首先使用socket()函数创建了一个TCP套接字,并将其赋值给sockfd变量。然后使用memset()函数将addr变量清零,并设置addr结构体的相关参数,包括地址族、IP地址、端口号等。接着使用connect()函数将套接字连接到指定的地址和端口号。如果连接失败,会使用perror()函数打印错误信息并退出程序。

需要注意的是,在使用connect()函数连接套接字时,服务端的套接字必须已经处于监听状态。否则,连接将失败。如果连接成功,connect()函数将返回0,否则返回-1,并设置errno变量以指示错误类型。可以使用perror()函数将errno对应的错误信息打印出来。

2.5、接受套接字连接        (tcp使用)

在Linux套接字编程中,服务端需要通过accept()函数接受客户端的连接请求。accept()函数的原型如下:

#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数说明:

  • sockfd:要接受连接的套接字文件描述符。
  • addr:指向客户端地址的指针。如果不关心客户端的地址,可以将该参数设置为NULL。
  • addrlen:客户端地址的长度。如果不关心客户端的地址,可以将该参数设置为NULL。
  • 返回值

 

下面是一个简单的例子,演示了如何创建一个TCP服务端,监听端口号为8080的套接字,并接受客户端的连接请求:

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

int main(int argc, char **argv) {
  int sockfd, connfd;
  struct sockaddr_in addr, cliaddr;
  socklen_t clilen;

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

  // 绑定套接字
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = htonl(INADDR_ANY);
  addr.sin_port = htons(8080);
  if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    perror("bind failed");
    exit(EXIT_FAILURE);
  }

  // 监听套接字
  if (listen(sockfd, 5) < 0) {
    perror("listen failed");
    exit(EXIT_FAILURE);
  }

  // 接受连接请求
  clilen = sizeof(cliaddr);
  connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &clilen);
  if (connfd < 0) {
    perror("accept failed");
    exit(EXIT_FAILURE);
  }

  // 其他操作...

  return 0;
}

在这个例子中,首先使用socket()函数创建了一个TCP套接字,并将其赋值给sockfd变量。然后使用memset()函数将addr变量清零,并设置addr结构体的相关参数,包括地址族、IP地址、端口号等。接着使用bind()函数将套接字绑定到指定的IP地址和端口号。如果绑定失败,会使用perror()函数打印错误信息并退出程序。然后使用listen()函数将套接字设置为监听状态,同时指定了等待连接的队列长度为5。如果监听失败,会使用perror()函数打印错误信息并退出程序。

最后,使用accept()函数接受客户端的连接请求。如果接受成功,accept()函数将返回一个新的套接字文件描述符,该文件描述符将用于与客户端进行通信。如果接受失败,会使用perror()函数打印错误信息并退出程序。注意,在调用accept()函数时,clilen参数应该初始化为cliaddr结构体的大小,该参数将被accept()函数用于返回

2.6、发送数据        (udp,tcp使用)

udp需要使用sendto和recvfrom函数,应该udp是无连接的,要指定地址

在套接字编程中,我们可以使用 send() 函数向已连接的套接字发送数据。send() 函数的参数包括:

  • sockfd:已连接套接字的文件描述符。
  • buf:指向要发送数据的缓冲区。
  • len:发送数据的长度。
  • flags:标志位,一般设置为0即可。

下面是一个使用已连接套接字发送数据的示例代码:

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

#define MAXLINE 1024

int main() {
    int sockfd;
    struct sockaddr_in servaddr;
    char buffer[MAXLINE];
    char *hello = "Hello from client";

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

    // 初始化服务器地址结构
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8080);

    // 连接服务器
    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("connection failed");
        exit(EXIT_FAILURE);
    }

    // 发送数据到服务器
    send(sockfd, hello, strlen(hello), 0);

    printf("Hello message sent.\n");

    // 关闭套接字
    close(sockfd);

    return 0;
}

在上面的示例代码中,我们首先创建了一个TCP套接字,并初始化了服务器地址结构体 servaddr。然后,我们通过调用 connect() 函数连接服务器。

在连接成功后,我们通过调用 send() 函数将字符串 "Hello from client" 发送给服务器。send() 函数的第一个参数是已连接套接字的文件描述符,第二个参数是要发送的数据缓冲区,第三个参数是要发送的数据长度,第四个参数是标志位,一般设置为0。

在发送完数据之后,我们关闭了TCP套接字。

2.7、接收数据        (udp,tcp使用)

在套接字编程中,我们可以使用 recv() 函数从已连接的套接字接收数据。recv() 函数的参数包括:

  • sockfd:已连接套接字的文件描述符。
  • buf:指向接收数据的缓冲区。
  • len:接收数据的长度。
  • flags:标志位,一般设置为0即可。

recv() 函数返回值表示接收到的字节数,如果返回值为0表示对方已经关闭了连接。如果返回值为负数,则表示发生了错误。

下面是一个使用已连接套接字接收数据的示例代码:

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

#define MAXLINE 1024

int main() {
    int sockfd, n;
    struct sockaddr_in servaddr;
    char buffer[MAXLINE];
    char *hello = "Hello from client";

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

    // 初始化服务器地址结构
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8080);

    // 连接服务器
    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("connection failed");
        exit(EXIT_FAILURE);
    }

    // 发送数据到服务器
    send(sockfd, hello, strlen(hello), 0);
    printf("Hello message sent.\n");

    // 接收服务器的回复
    n = recv(sockfd, buffer, MAXLINE, 0);
    buffer[n] = '\0';
    printf("Server : %s\n", buffer);

    // 关闭套接字
    close(sockfd);

    return 0;
}

在上面的示例代码中,我们首先创建了一个TCP套接字,并初始化了服务器地址结构体 servaddr。然后,我们通过调用 connect() 函数连接服务器。

在连接成功后,我们通过调用 send() 函数将字符串 "Hello from client" 发送给服务器。然后,我们通过调用 recv() 函数接收服务器的回复。recv() 函数的第一个参数是已连接套接字的文件描述符,第二个参数是接收数据的缓冲区,第三个参数是接收数据的长度,第四个参数是标志位,一般设置为0。

在接收完数据之后,我们关闭了TCP套接字。

2.8、关闭套接字        (udp,tcp使用)

在Linux套接字编程中,关闭套接字需要使用close()函数。close()函数的原型如下:

#include <unistd.h>

int close(int sockfd);

参数说明:

  • sockfd:要关闭的套接字文件描述符。

2.9、 客户端/服务器模型

linux上的单线程udp协议的客户端

#include<iostream>
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<unistd.h>
#include<arpa/inet.h>
using namespace std;

int main()
{
    // create socket
    int socketfd = socket(AF_INET, SOCK_DGRAM, 0); // UDP
    if(socketfd < 0)
    {
        cout << "socket error" << endl;
        return -1;
    }
    
    // 解析服务端地址
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(34567);

    // send & recv
    while(1)
    {
        char buff[1024];
        cin >> buff;
        sendto(socketfd, buff, sizeof(buff), 0, (struct sockaddr*)&addr, sizeof(addr));
        socklen_t len = sizeof(addr);
        recvfrom(socketfd, buff, sizeof(buff), 0, (struct sockaddr*)&addr, &len);
        cout << buff << endl;
    }

    // close
    close(socketfd);
    return 0;
}

linux上的单线程udp协议的服务端

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
using namespace std;

int main()
{
    // create socket
    int socketfd = socket(AF_INET, SOCK_DGRAM, 0); // UDP
    if (socketfd < 0)
    {
        cout << "socket error" << endl;
        return -1;
    }

    // bind socket
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;         // IPv4
    addr.sin_port = htons(34567);      // port
    addr.sin_addr.s_addr = INADDR_ANY; // IP address
    if (bind(socketfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        cout << "bind error" << endl;
        return -1;
    }

    // send & recv
    while (1)
    {
        char buff[1024];
        sockaddr_in client_addr; // 客户端地址
        socklen_t len = sizeof(client_addr);
        recvfrom(socketfd, buff, sizeof(buff), 0, (struct sockaddr *)&client_addr, &len); // 获取客户端地址信息
        cout << buff << endl;
        sendto(socketfd, buff, sizeof(buff), 0, (struct sockaddr *)&client_addr, len); // 发送消息到客户端地址
    }

    // close
    close(socketfd);
    return 0;
}

linux上的单线程tcp协议的客户端

#include<iostream>
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<unistd.h>
#include<arpa/inet.h>
using namespace std;

int main()
{
    // create socket
    int socketfd = socket(AF_INET, SOCK_STREAM, 0); // UDP
    if(socketfd < 0)
    {
        cout << "socket error" << endl;
        return -1;
    }
    
    // 解析服务端地址
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(34567);

    // connect
    if(connect(socketfd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
    {
        cout << "connect error" << endl;
        return -1;
    }

    // send & recv
    while(1)
    {
        char buff[1024];
        cin >> buff;
        send(socketfd, buff, sizeof(buff), 0);
        recv(socketfd, buff, sizeof(buff), 0);
        cout << buff << endl;
    }

    // close
    close(socketfd);
    return 0;
}

linux上的单线程tcp协议的服务器

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
using namespace std;

int main()
{
    // create socket
    int socketfd = socket(AF_INET, SOCK_STREAM, 0); // TCP
    if (socketfd < 0)
    {
        cout << "socket error" << endl;
        return -1;
    }

    // bind socket
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;         // IPv4
    addr.sin_port = htons(34567);      // port
    addr.sin_addr.s_addr = INADDR_ANY; // IP address
    if (bind(socketfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        cout << "bind error" << endl;
        return -1;
    }

    // listen
    if (listen(socketfd, 5) < 0)
    {
        cout << "listen error" << endl;
        return -1;
    }

    // accept
    sockaddr_in client_addr; // 客户端地址
    socklen_t len = sizeof(client_addr);
    int clientfd = accept(socketfd, (struct sockaddr *)&client_addr, &len);
    if (clientfd < 0)
    {
        cout << "accept error" << endl;
        return -1;
    }

    // send & recv
    while (1)
    {
        char buff[1024];
        recv(clientfd, buff, sizeof(buff), 0);
        cout << buff << endl;
        send(clientfd, buff, sizeof(buff), 0);
    }

    // close
    close(socketfd);
    return 0;
}
  • 9
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一起慢慢变强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值