Unix Day13

1 基于套接字的进程间通信
2 基于套接字的网络通信
3 基于TCP协议的客户机与服务器
1 基于套接字的进程间通信
1.1 问题
套接字(socket)是一个基于TCP/IP协议可实现基本网络通信功能的逻辑对象。机器与机器的通信,或者进程与进程的通信,在这里都可以被抽象地看作是套接字与套接字的通信。应用程序编写者无需了解网络协议的任何细节,更无需知晓系统内核和网络设备的运作机制,只要把想发送的数据写入套接字,或从套接字中读取想接收的数据即可。从这个意义上讲,套接字就相当于一个文件描述符,而网络就是一种特殊的文件,面向网络的编程与面向文件的编程已没有分别,而这恰恰是Unix系统一切皆文件思想的又一例证。

1.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:服务器

代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int sockfd = socket (AF_LOCAL, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_un addr;
addr.sun_family = AF_LOCAL;
strcpy (addr.sun_path, “mysock”);
if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“bind”);
unlink(“mysock”);
exit (EXIT_FAILURE);
}
char text[1024];
ssize_t readed = read (sockfd, text, 1024);
if (readed == -1)
{
perror (“read”);
unlink(“mysock”);
exit (EXIT_FAILURE);
}
printf(“客户机发送内容:%s\n”, text);
if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
unlink(“mysock”);
return 0;
}
上述代码中,以下代码:

int sockfd = socket (AF_LOCAL, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
使用函数socket创建套接字,该函数有三个参数,说明如下:

第一个参数为通信域,即协议族,可取以下值:

AF_LOCAL/AF_UNIX - 本地通信,即进程间通信

AF_INET - 基于IPv4的网络通信

AF_INET6 - 基于IPv6的网络通信

AF_PACKET - 基于底层包接口的网络通信

第二个参数为套接字类型,可取以下值

SOCK_STREAM - 流式套接字,即使用TCP协议的套接字

SOCK_DGRAM - 数据报套接字,即使用UDP协议的套接字

SOCK_RAW - 原始套接字,即使用IP协议的套接字

第三个参数为特殊协议,通常不用,取0即可。

上述代码中,以下代码:

struct sockaddr_un addr;
addr.sun_family = AF_LOCAL;
strcpy (addr.sun_path, “mysock”);
准备地址结构,使用sockaddr_un结构体类型。

上述代码中,以下代码:

if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“bind”);
unlink(“mysock”);
exit (EXIT_FAILURE);
}
使用函数bind将套接字对象和自己的地址结构绑定在一起,该函数有三个参数,说明如下:

第一个参数为套接字描述符。

第二个参数为自己的地址结构。

第三个参数为地址结构长度(以字节为单位)。

上述代码中,以下代码:

char text[1024];
ssize_t readed = read (sockfd, text, 1024);
if (readed == -1)
{
perror (“read”);
unlink(“mysock”);
exit (EXIT_FAILURE);
}
printf(“客户机发送内容:%s\n”, text);
使用函数read通过套接字接收字节流。该函数有三个参数,说明如下:

第一个参数为套接字描述符

第二个参数为内存缓冲区

第三个参数为期望读取的字节数

上述代码中,以下代码:

if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
使用函数close关闭处于打开状态的套接字描述符,该函数的参数为处于打开状态的套接字描述符。

上述代码中,以下代码:

unlink("mysock");

使用函数unlink删除不再使用的套接字文件。

步骤二:客户端

代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int sockfd = socket (AF_LOCAL, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_un addr;
addr.sun_family = AF_LOCAL;
strcpy (addr.sun_path, “mysock”);
if (connect (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“connect”);
exit (EXIT_FAILURE);
}
char text[1024] = “你好,服务器”;
ssize_t written = write (sockfd, text, strlen(text));
if (written == -1)
{
perror (“write”);
exit (EXIT_FAILURE);
}
if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:

int sockfd = socket (AF_LOCAL, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
使用函数socket创建套接字。

上述代码中,以下代码:

struct sockaddr_un addr;
addr.sun_family = AF_LOCAL;
strcpy (addr.sun_path, “mysock”);
准备地址结构,使用sockaddr_un结构体类型。

上述代码中,以下代码:

if (connect (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“connect”);
exit (EXIT_FAILURE);
}
使用函数connect将套接字对象和对方的地址结构连接在一起。该函数有三个参数,说明如下:

第一个参数为套接字描述符

第二个参数为对方的地址结构

第三个参数为地址结构长度(以字节为单位)

上述代码中,以下代码:

char text[1024] = “你好,服务器”;
ssize_t written = write (sockfd, text, strlen(text));
if (written == -1)
{
perror (“write”);
exit (EXIT_FAILURE);
}
使用函数write通过套接字发送字节流。该函数有三个参数,说明如下:

第一个参数为套接字描述符

第二个参数为内存缓冲区

第三个参数为期望发送的字节数

上述代码中,以下代码:

if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
使用函数close关闭处于打开状态的套接字描述符,该函数的参数为处于打开状态的套接字描述符。

1.3 完整代码
本案例的完整代码如下所示:

服务器,代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int sockfd = socket (AF_LOCAL, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_un addr;
addr.sun_family = AF_LOCAL;
strcpy (addr.sun_path, “mysock”);
if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“bind”);
unlink(“mysock”);
exit (EXIT_FAILURE);
}
char text[1024];
ssize_t readed = read (sockfd, text, 1024);
if (readed == -1)
{
perror (“read”);
unlink(“mysock”);
exit (EXIT_FAILURE);
}
printf(“客户机发送内容:%s\n”, text);
if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
unlink(“mysock”);
return 0;
}
客户端,代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int sockfd = socket (AF_LOCAL, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_un addr;
addr.sun_family = AF_LOCAL;
strcpy (addr.sun_path, “mysock”);
if (connect (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“connect”);
exit (EXIT_FAILURE);
}
char text[1024] = “你好,服务器”;
ssize_t written = write (sockfd, text, strlen(text));
if (written == -1)
{
perror (“write”);
exit (EXIT_FAILURE);
}
if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
return 0;
}
2 基于套接字的网络通信
2.1 问题
套接字(socket)是一个基于TCP/IP协议可实现基本网络通信功能的逻辑对象。机器与机器的通信,或者进程与进程的通信,在这里都可以被抽象地看作是套接字与套接字的通信。应用程序编写者无需了解网络协议的任何细节,更无需知晓系统内核和网络设备的运作机制,只要把想发送的数据写入套接字,或从套接字中读取想接收的数据即可。从这个意义上讲,套接字就相当于一个文件描述符,而网络就是一种特殊的文件,面向网络的编程与面向文件的编程已没有分别,而这恰恰是Unix系统一切皆文件思想的又一例证。

2.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:服务器

代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“bind”);
exit (EXIT_FAILURE);
}
char text[1024];
ssize_t readed = read (sockfd, text, 1024);
if (readed == -1)
{
perror (“read”);
exit (EXIT_FAILURE);
}
printf(“客户机发送内容:%s\n”, text);
if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:

int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
使用函数socket创建套接字。

上述代码中,以下代码:

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = INADDR_ANY;
准备地址结构,使用sockaddr_in结构体类型。

上述代码中,以下代码:

if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“bind”);
exit (EXIT_FAILURE);
}
使用函数bind将套接字对象和自己的地址结构绑定在一起。

上述代码中,以下代码:

char text[1024];
ssize_t readed = read (sockfd, text, 1024);
if (readed == -1)
{
perror (“read”);
exit (EXIT_FAILURE);
}
printf(“客户机发送内容:%s\n”, text);
使用函数read通过套接字接收字节流。

上述代码中,以下代码:

if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
使用函数close关闭处于打开状态的套接字描述符,该函数的参数为处于打开状态的套接字描述符。

步骤二:客户端

代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = inet_addr(“127.0.0.1”);
if (connect (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“connect”);
exit (EXIT_FAILURE);
}
char text[1024] = “你好,服务器”;
ssize_t written = write (sockfd, text, strlen(text));
if (written == -1)
{
perror (“write”);
exit (EXIT_FAILURE);
}
if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:

int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
使用函数socket创建套接字。

上述代码中,以下代码:

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = inet_addr(“127.0.0.1”);
准备地址结构,使用sockaddr_in结构体类型。

上述代码中,以下代码:

if (connect (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“connect”);
exit (EXIT_FAILURE);
}
使用函数connect将套接字对象和对方的地址结构连接在一起。

上述代码中,以下代码:

char text[1024] = “你好,服务器”;
ssize_t written = write (sockfd, text, strlen(text));
if (written == -1)
{
perror (“write”);
exit (EXIT_FAILURE);
}
使用函数write通过套接字发送字节流。

上述代码中,以下代码:

if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
使用函数close关闭处于打开状态的套接字描述符,该函数的参数为处于打开状态的套接字描述符。

2.3 完整代码
本案例的完整代码如下所示:

服务器,代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“bind”);
exit (EXIT_FAILURE);
}
char text[1024];
ssize_t readed = read (sockfd, text, 1024);
if (readed == -1)
{
perror (“read”);
exit (EXIT_FAILURE);
}
printf(“客户机发送内容:%s\n”, text);
if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
return 0;
}
客户端,代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
int sockfd = socket (AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = inet_addr(“127.0.0.1”);
if (connect (sockfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“connect”);
exit (EXIT_FAILURE);
}
char text[1024] = “你好,服务器”;
ssize_t written = write (sockfd, text, strlen(text));
if (written == -1)
{
perror (“write”);
exit (EXIT_FAILURE);
}
if (close (sockfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
return 0;
}
3 基于TCP协议的客户机与服务器
3.1 问题
一个完整TCP通信过程需要依次经历三个阶段:

首先,客户机必须建立与服务器的连接,所谓虚电路。

然后,凭借已建立好的连接,通信双方相互交换数据。

最后,客户机与服务器双双终止连接,结束通信过程。

3.2 步骤
实现此案例需要按照如下步骤进行。

步骤一:服务器

代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
int listenfd = socket (AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind (listenfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“bind”);
exit (EXIT_FAILURE);
}
if (listen (listenfd, 1024) == -1)
{
perror (“listen”);
exit (EXIT_FAILURE);
}
struct sockaddr_in addrcli = {};
socklen_t addrlen = sizeof (addrcli);
int connfd = accept (listenfd, (struct sockaddr*)&addrcli, &addrlen);
if (connfd == -1)
{
perror (“accept”);
exit (EXIT_FAILURE);
}
printf (“服务器已接受来自%s:%hu客户机的连接请求\n”, inet_ntoa (addrcli.sin_addr),ntohs (addrcli.sin_port));
char buf[1024];
ssize_t rcvd = recv (connfd, buf, sizeof (buf), 0);
if (rcvd == -1)
{
perror (“recv”);
exit (EXIT_FAILURE);
}
if (rcvd == 0)
{
printf (“客户机已关闭连接\n”);
exit (EXIT_FAILURE);
}
buf[rcvd] = ‘\0’;
printf (“客户端说:%s\n”, buf);
printf (“服务器说:”);
gets (buf);
ssize_t sent = send (connfd, buf, strlen (buf) * sizeof (buf[0]), 0);
if (sent == -1)
{
perror (“send”);
exit (EXIT_FAILURE);
}
if (close (listenfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
if (close (connfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:

int listenfd = socket (AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
使用函数socket创建套接字。

上述代码中,以下代码:

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = INADDR_ANY;
准备地址结构,使用sockaddr_in结构体类型。

上述代码中,以下代码:

if (bind (listenfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“bind”);
exit (EXIT_FAILURE);
}
使用函数bind将套接字对象和自己的地址结构绑定在一起。

上述代码中,以下代码:

if (listen (listenfd, 1024) == -1)
{
perror (“listen”);
exit (EXIT_FAILURE);
}
使用函数listen在指定套接字上启动对连接请求的侦听。该函数有两个参数,说明如下:

第一个参数为套接字描述符。

第二个参数为连接请求的最大值。

上述代码中,以下代码:

struct sockaddr_in addrcli = {};
socklen_t addrlen = sizeof (addrcli);
int connfd = accept (listenfd, (struct sockaddr*)&addrcli, &addrlen);
if (connfd == -1)
{
perror (“accept”);
exit (EXIT_FAILURE);
}
printf (“服务器已接受来自%s:%hu客户机的连接请求\n”, inet_ntoa (addrcli.sin_addr),ntohs (addrcli.sin_port));
使用函数accept在指定套接字上等待并接受连接请求。该函数有三个参数,说明如下:

第一个参数为套接字描述符。

第二个参数为输出连接请求发起者地址结构。

第三个参数为输入/输出,连接请求发起者地址结构长度(以字节为单位)。

上述代码中,以下代码:

char buf[1024];
ssize_t rcvd = recv (connfd, buf, sizeof (buf), 0);
if (rcvd == -1)
{
perror (“recv”);
exit (EXIT_FAILURE);
}
if (rcvd == 0)
{
printf (“客户机已关闭连接\n”);
exit (EXIT_FAILURE);
}
buf[rcvd] = ‘\0’;
printf (“客户端说:%s\n”, buf);
使用函数recv通过指定套接字接收数据,该函数有四个参数,说明如下:

第一个参数为套接字描述符。

第二个参数为应用程序接收缓冲区。

第三个参数为期望接收的字节数。

第四个参数为接收标志,一般取0,还可取以下值:

MSG_DONTWAIT - 以非阻塞方式接受数据。

MSG_OOB - 接收带外数据。

MSG_PEEK - 只查看可接收的数据,函数返回后数据依然留在接收缓冲区中。

MSG_WAITALL - 等待所有数据,即不接收到len字节的数据,函数就不返回。

上述代码中,以下代码:

printf ("服务器说:");
gets (buf);
ssize_t sent = send (connfd, buf, strlen (buf) * sizeof (buf[0]), 0);

if (sent == -1)
{
perror (“send”);
exit (EXIT_FAILURE);
}
使用函数send通过指定套接字发送数据,该函数有四个参数,说明如下:

第一个参数为套接字描述符。

第二个参数为应用程序发送缓冲区。

第三个参数为期望发送的字节数。

第四个参数为接收标志,一般取0,还可取以下值:

MSG_DONTWAIT - 以非阻塞方式发送数据。

MSG_OOB - 发送带外数据。

MSG_DONTROUTE - 不查路由表,直接在本地网络中寻找目的主机

上述代码中,以下代码:

if (close (listenfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
if (close (connfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
使用函数close关闭处于打开状态的套接字描述符,该函数的参数为处于打开状态的套接字描述符。

步骤二:客户端

代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
int listenfd = socket (AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = inet_addr(“127.0.0.1”);
if (connect (listenfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“connect”);
exit (EXIT_FAILURE);
}
char buf[1024] = “你好,服务器”;
printf (“客户端说:%s\n”, buf);
ssize_t sent = send (listenfd, buf, strlen (buf) * sizeof (buf[0]), 0);
if (sent == -1)
{
perror (“send”);
exit (EXIT_FAILURE);
}
ssize_t rcvd = recv (listenfd, buf, sizeof (buf), 0);
if (rcvd == -1)
{
perror (“recv”);
exit (EXIT_FAILURE);
}
buf[rcvd] = ‘\0’;
printf (“服务器说:%s\n”, buf);
if (close (listenfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
return 0;
}
上述代码中,以下代码:

int listenfd = socket (AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
使用函数socket创建套接字。

上述代码中,以下代码:

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = inet_addr(“127.0.0.1”);
准备地址结构,使用sockaddr_in结构体类型。

上述代码中,以下代码:

if (connect (listenfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“connect”);
exit (EXIT_FAILURE);
}
使用函数connect将套接字对象和对方的地址结构连接在一起。

上述代码中,以下代码:

char buf[1024] = “你好,服务器”;
printf (“客户端说:%s\n”, buf);
ssize_t sent = send (listenfd, buf, strlen (buf) * sizeof (buf[0]), 0);
if (sent == -1)
{
perror (“send”);
exit (EXIT_FAILURE);
}
使用函数send通过指定套接字发送数据。

上述代码中,以下代码:

ssize_t rcvd = recv (listenfd, buf, sizeof (buf), 0);

if (rcvd == -1)
{
perror (“recv”);
exit (EXIT_FAILURE);
}
buf[rcvd] = ‘\0’;
printf (“服务器说:%s\n”, buf);
使用函数recv通过指定套接字接收数据。

上述代码中,以下代码:

if (close (listenfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
使用函数close关闭处于打开状态的套接字描述符,该函数的参数为处于打开状态的套接字描述符。

3.3 完整代码
本案例的完整代码如下所示:

服务器,代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
int listenfd = socket (AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind (listenfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“bind”);
exit (EXIT_FAILURE);
}
if (listen (listenfd, 1024) == -1)
{
perror (“listen”);
exit (EXIT_FAILURE);
}
struct sockaddr_in addrcli = {};
socklen_t addrlen = sizeof (addrcli);
int connfd = accept (listenfd, (struct sockaddr*)&addrcli, &addrlen);
if (connfd == -1)
{
perror (“accept”);
exit (EXIT_FAILURE);
}
printf (“服务器已接受来自%s:%hu客户机的连接请求\n”, inet_ntoa (addrcli.sin_addr),ntohs (addrcli.sin_port));
char buf[1024];
ssize_t rcvd = recv (connfd, buf, sizeof (buf), 0);
if (rcvd == -1)
{
perror (“recv”);
exit (EXIT_FAILURE);
}
if (rcvd == 0)
{
printf (“客户机已关闭连接\n”);
exit (EXIT_FAILURE);
}
buf[rcvd] = ‘\0’;
printf (“客户端说:%s\n”, buf);
printf (“服务器说:”);
gets (buf);
ssize_t sent = send (connfd, buf, strlen (buf) * sizeof (buf[0]), 0);
if (sent == -1)
{
perror (“send”);
exit (EXIT_FAILURE);
}
if (close (listenfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
if (close (connfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
return 0;
}
客户端,代码如下所示:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
int listenfd = socket (AF_INET, SOCK_STREAM, 0);
if (listenfd == -1)
{
perror (“socket”);
exit (EXIT_FAILURE);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons (8888);
addr.sin_addr.s_addr = inet_addr(“127.0.0.1”);
if (connect (listenfd, (struct sockaddr*)&addr, sizeof (addr)) == -1)
{
perror (“connect”);
exit (EXIT_FAILURE);
}
char buf[1024] = “你好,服务器”;
printf (“客户端说:%s\n”, buf);
ssize_t sent = send (listenfd, buf, strlen (buf) * sizeof (buf[0]), 0);
if (sent == -1)
{
perror (“send”);
exit (EXIT_FAILURE);
}
ssize_t rcvd = recv (listenfd, buf, sizeof (buf), 0);
if (rcvd == -1)
{
perror (“recv”);
exit (EXIT_FAILURE);
}
buf[rcvd] = ‘\0’;
printf (“服务器说:%s\n”, buf);
if (close (listenfd) == -1)
{
perror (“close”);
exit (EXIT_FAILURE);
}
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值