1-函数声明

函数声明

连接工作

服务器

1.socket函数

​ 头文件:<sys/socket.h>

​ 创建一个未被使用的socket,并进行初始化协议。

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

参数:

  • domain:指定协议族

    • AF_INET表示IPv4协议族
    • AF_INET6表示IPv6协议族。
  • type:指定套接字类型

    • SOCK_STREAM表示基于TCP协议的流式套接字
    • SOCK_DGRAM表示基于UDP协议的数据报套接字等。
  • protocol:指定协议类型,在通常为0表示使用默认协议。

    • IPPROTO_TCP:TCP传输协议。
    • IPPROTO_UDP:UDP传输协议。
    • IPPROTO_SCTP:SCTP传输协议。
    • IPPROTO_ICMP:ICMP协议。
    • IPPROTO_RAW:原始IP数据包协议。
    • IPPROTO_IP:IP协议,通常用于在IPv4和IPv6之间进行区分。

函数返回值:为新建套接字的描述符。如果失败,将返回-1并设置errno变量。

2.bind函数

​ 头文件:

​ 将socket和服务器的地址绑定。

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

参数:

  • sockfd

  • myaddr:指向一个标志ip和端口号的结构体。在为结构体赋值时使用sockaddr_in,使用时强转为该结构体类型。

    •   struct sockaddr_in {
            sa_family_t    sin_family;  // 协议族,始终为AF_INET
            in_port_t      sin_port;    // 端口号
            struct in_addr sin_addr;    // IPv4地址信息
            char           sin_zero[8]; // 未使用,填充0
        };
        

      * ```C++
      servaddr.sin_family = AF_INET; // 协议族,在socket编程中只能是AF_INET。
      servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 任意ip地址。
      //servaddr.sin_addr.s_addr = inet_addr("118.89.50.198"); // 指定ip地址。
      servaddr.sin_port = htons(5051); // 指定通信端口。
  • addrlen:第二个参数的大小,sizeof()

返回值:

  • 0,成功

  • -1,失败

3.listen函数

​ 将socket设置为监听模式。

int listen(int sockfd, int backlog);

参数:

  • sockfd:socket

  • backlog:等待队列的长度

返回值:

  • 0,成功

  • -1,失败

4.accept函数

​ 从一个监听模式的socket,等待队列中取出一个指代客户端的socket。

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

参数:

  • sockfd:监听socket

  • addr:接收客户端地址的结构体(的地址)

  • addrlen:addr的长度(的地址)

返回值:

  • socket,成功

  • -1,失败

客户端

1.socket(略)

2.connect函数

int connect(int sockfd, struct sockaddr *secv_addr, int addrlen);

参数:

  • sockfd:指代服务器的socket

  • secv_addr:服务器的地址和端口(的结构体)

  • addrlen:结构体的大小

返回值:

  • 0,成功

  • -1,失败

收发消息(服务器和客户端)

1.发送(send/write)

​ 在Linux下,socket也是一个文件描述符,因此可以使用对文件操作的函数来发送消息。

1.1 send函数(默认阻塞)

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • sockfd:指代客户端的socket

  • buf:指向要发送的数据缓冲区的指针。

  • len:指定要发送的数据长度。

  • flags:指定选项标志,填0。常见的标志有:

    • MSG_OOB:发送带外数据。
    • MSG_NOSIGNAL:在出现错误时不产生SIGPIPE信号。

返回值:

  • 实际发送的数据长度,如果失败则返回-1并设置errno变量

1.2 write函数(阻塞)

​ 头文件:<unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

参数:

  • fd:指定要写入数据的文件描述符。

  • buf:指向要写入的数据缓冲区的指针。

  • count:指定要写入的数据长度。

返回值:

  • 为实际写入的数据长度,如果失败则返回-1并设置errno变量

2.接收(recv/read)

2.1 recv函数

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数:

  • sockfd:指定已连接的套接字描述符,即要接收数据的来源。

  • buf:指向接收数据缓冲区的指针。

  • len:指定接收数据缓冲区的长度。接收的数据只会比这个少。

  • flags:指定选项标志,填0。常见的标志有:

    • MSG_OOB:接收带外数据。
    • MSG_PEEK:接收数据但不将其从输入队列中删除。

返回值:

  • 接收的数据长度

  • -1,失败

  • 0,socket被关闭

2.2 read函数

ssize_t read(int fd, void *buf, size_t count);

参数:

  • fd:指定要读取数据的文件描述符。

  • buf:指向接收数据缓冲区的指针。

  • count:指定接收数据缓冲区的长度。

返回值:

  • 为实际读取到的数据长度,如果失败则返回-1并设置errno变量

收尾工作

1.close函数

​ 头文件:<unistd.h>

​ 关闭socket。

int close(int socket);

参数:

  • socket:需要关闭的socket

返回值:

  • -1,失败

  • 0,成功

案例

服务器

/*
* 程序名:book242.cpp,此程序用于演示socket通信的服务端
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main()
{
// 第1步:创建服务端的socket。
int listenfd;
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
return -1;
}

// 第2步:把服务端用于通信的地址和端口绑定到socket上。
struct sockaddr_in servaddr; // 服务端地址信息的数据结构。
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; // 协议族,在socket编程中只能是AF_INET。
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 任意ip地址。
// servaddr.sin_addr.s_addr = inet_addr("118.89.50.198"); // 指定ip地址。
servaddr.sin_port = htons(5051); // 指定通信端口。
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0)
{
perror("bind");
close(listenfd);
return -1;
}

// 第3步:把socket设置为监听模式。
if (listen(listenfd, 5) != 0)
{
perror("listen");
close(listenfd);
return -1;
}

// 第4步:接受客户端的连接。
int clientfd; // 客户端的socket。
int socklen = sizeof(struct sockaddr_in); // struct sockaddr_in的大小
struct sockaddr_in clientaddr; // 客户端的地址信息。
clientfd = accept(listenfd, (struct sockaddr *)&clientaddr, (socklen_t *)&socklen);
printf("客户端(%s)已连接。\n", inet_ntoa(clientaddr.sin_addr));

// 第5步:与客户端通信,接收客户端发过来的报文后,回复ok。
char buffer[1024];
while (1)
{
int iret;
memset(buffer, 0, sizeof(buffer));
if ((iret = recv(clientfd, buffer, sizeof(buffer), 0)) <= 0) // 接收客户端的请求报文。
{
printf("iret=%d\n", iret);
break;
}
printf("ret=%d,接收:%s\n", iret, buffer);

strcpy(buffer, "ok");
if ((iret = send(clientfd, buffer, strlen(buffer), 0)) <= 0) // 向客户端发送响应结果。
{
perror("send");
break;
}
printf("iret=%d,发送:%s\n", iret, buffer);
}

// 第6步:关闭socket,释放资源。
close(listenfd);
close(clientfd);
}

客户端

/*
* 程序名:book241.cpp,此程序用于演示socket的客户端
* 作者:C语言技术网(www.freecplus.net) 日期:20190525
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main()
{
// 第1步:创建客户端的socket。
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
return -1;
}

// 第2步:向服务器发起连接请求。
struct hostent *h;
if ((h = gethostbyname("192.168.190.133")) == 0) // 指定服务端的ip地址。
{
printf("gethostbyname failed.\n");
close(sockfd);
return -1;
}
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5051); // 指定服务端的通信端口。
memcpy(&servaddr.sin_addr, h->h_addr, h->h_length);
if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) // 向服务端发起连接清求。
{
perror("connect");
close(sockfd);
return -1;
}
char buffer[1024];

// 第3步:与服务端通信,发送一个报文后等待回复,然后再发下一个报文。
for (int ii = 0; ii < 3; ii++)
{
int iret;
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "这是第%d个超级女生,编号%03d。", ii + 1, ii + 1);
if ((iret = send(sockfd, buffer, strlen(buffer), 0)) <= 0) // 向服务端发送请求报文。
{
perror("send");
break;
}
printf("iret=%d,发送:%s\n", iret, buffer);

memset(buffer, 0, sizeof(buffer));
if ((iret = recv(sockfd, buffer, sizeof(buffer), 0)) <= 0) // 接收服务端的回应报文。
{
printf("iret=%d\n", iret);
break;
}
printf("ret=%d,接收:%s\n", iret, buffer);
}

// 第4步:关闭socket,释放资源。
close(sockfd);
}