#include /* See NOTES */
#include
int listen(int sockfd, int backlog);
返回值:成功返回 0,失败返回 -1
#include /* See NOTES */
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
返回值:成功返回连接套接字描述符,失败返回 -1
#include
#include
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
返回值:成功返回实际接收到的字节数,失败返回 -1
#include
#include
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
返回值:成功返回实际被发送的字节数,失败返回 -1
服务器主进程关闭连接套接字;服务器子进程关闭侦听套接字。主进程通过循环继续阻塞于针对侦听套接字的 accept 调用,而子进程则通过连接套接字与客户机通信
套接字描述符与普通的我那件描述符一样,是带有引用计数的。在一个套接字描述符上调用 close 函数,并不一定真的关闭该套接字,而只是将其引用计数减一。只有当套接字描述符的引用计数被减到零时,才真的会释放该套接字对象所占用的资源,并向对方发送 FIN 分节。因此服务器主进程关闭连接套接字,并不会影响子进程通过该套接字与客户机通信。同理,服务器子进程关闭侦听套接字也不会影响主进程通过套接字继续等待连接。
如果服务器主进程在创建子进程后不关闭连接套接字,一方面将耗尽其可用文件描述符;令一方面在子进程结束通信关闭链接套接字时,其描述符上的引用计数只会由 2 变成 1,而不会变成 0,TCP 协议栈将永远保持此连接。
基于 TCP 协议的客户机与服务器
//服务器 tcpA.c
#include
#include
#include
#include
#include
#include
#include
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;
}
//客户端 tcpB.c
#include
#include
#include
#include
#include
#include
#include
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;
}
输出结果:
在一个终端执行:
# ./tcpA
服务器已接受来自127.0.0.1:41428客户机的连接请求
客户端说:你好,服务器
服务器说:hello
另一个终端执行:
# ./tcpB
客户端说:你好,服务器
服务器说:hello
#include
#include
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
返回值:成功返回实际接收到的字节数,失败返回 -1
#include
#include
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
返回值:成功返回实际被发送的字节数,失败返回 -1
基于 UDP 协议的客户机和服务器
//服务器 udpA.c
#include
#include
#include
#include
#include
#include
#include
int main()
{
int listenfd = socket (AF_INET, SOCK_DGRAM, 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);
}
char buf[1024];
struct sockaddr_in addrcli = {};
socklen_t addrlen = sizeof (addrcli);
ssize_t rcvd = recvfrom (listenfd, buf, sizeof (buf), 0, (struct sockaddr*)&addrcli, &addrlen);
if (rcvd == -1)
{
perror ("recvfrom");
exit (EXIT_FAILURE);
}
buf[rcvd] = '\0';
printf ("客户端说:%s\n", buf);
printf ("服务器说:");
gets (buf);
ssize_t sent = sendto (listenfd, buf, strlen (buf) * sizeof (buf[0]), 0, (struct sockaddr*)&addrcli, sizeof (addrcli));
if (sent == -1)
{
perror ("send");
exit (EXIT_FAILURE);
}
if (close (listenfd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
//客户端 udpB.c
#include
#include
#include
#include
#include
#include
#include
int main()
{
int listenfd = socket (AF_INET, SOCK_DGRAM, 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");
char buf[1024] = "你好,服务器";
printf ("客户端说:%s\n", buf);
ssize_t sent = sendto (listenfd, buf, strlen (buf) * sizeof (buf[0]), 0, (struct sockaddr*)&addr, sizeof (addr));
if (sent == -1)
{
perror ("send");
exit (EXIT_FAILURE);
}
struct sockaddr_in addrser = {};
socklen_t addrlen = sizeof (addrser);
ssize_t rcvd = recvfrom (listenfd, buf, sizeof (buf), 0, (struct sockaddr*)&addrser, &addrlen);
if (rcvd == -1)
{
perror ("recvfrom");
exit (EXIT_FAILURE);
}
buf[rcvd] = '\0';
printf ("服务器说:%s\n", buf);
if (close (listenfd) == -1)
{
perror ("close");
exit (EXIT_FAILURE);
}
return 0;
}
输出结果:
在一个终端执行:
# ./udpA
客户端说:你好,服务器
服务器说:hello
另一个终端执行:
# ./udpB
客户端说:你好,服务器
服务器说:hello