UNPv13:#第5章#TCP客户/服务器程序示例

Code

github

//server.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

void sig_chld(int signo)
{
    pid_t pid;
    int stat;
    while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
        printf("child %d terminated\n", pid);

    return;
}

int main(int argc, char* argv[])
{
    int listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listenfd == -1)
    {
        perror("function socket error :");
        exit(-1);
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(atoi(argv[1]));
    if(bind(listenfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)
    {
        perror("function bind error : ");
        exit(-1);
    }

    if(listen(listenfd, 5) == -1)
    {
        perror("function listen error : ");
        exit(-1);
    }

    struct sigaction act, oact;
    act.sa_handler = sig_chld;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if(sigaction(SIGCHLD, &act, &oact) == -1)
    {
        perror("function sigaction error : ");
        exit(-1);
    }

    int connfd;
    struct sockaddr_in clientaddr;
    socklen_t clientlen;
    while(1)
    {
        clientlen = sizeof(clientaddr);
        connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clientlen);
        if(connfd == -1)
        {
            if(errno == EINTR)
                continue;
            perror("function accept error : ");
            exit(-1);
        }

        if(fork() == 0)
        {
            if(close(listenfd) == -1)
            {
                perror("function close error : ");
                exit(-1);
            }

            char buf[1024];
            int len;
        AGAIN:
            while((len = read(connfd, buf, 1024)) > 0)
                write(connfd, buf, len);
            if(len < 0 && errno == EINTR)
                goto AGAIN;
            else if(len < 0)
            {
                perror("function read error : ");
                exit(1);
            }
        }
        else
        {
            if(close(connfd) == -1)
            {
                perror("function close error : ");
                exit(-1);
            }
        }
    }
    return 0;
}
//client.c

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

int main(int argc, char* argv[])
{
    int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sockfd == -1)
    {
        perror("function socket error : ");
        exit(-1);
    }

    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    switch(inet_pton(AF_INET, argv[1], &serveraddr.sin_addr))
    {
    case 0:
        perror("function inet_pton error (input invalid) : ");
        exit(-1);
        break;
    case 1:
        //successed
        break;
    default:
        perror("function inet_pton error : ");
        exit(-1);
        break;
    }
    serveraddr.sin_port = htons(atoi(argv[2]));
    connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

    char buf[1024];
    while(fgets(buf, 1024, stdin) != NULL)
    {
        write(sockfd, buf, strlen(buf));
        if(read(sockfd, buf, 1024) == 0)
        {
            perror("server terminated");
            exit(0);
        }
        fputs(buf, stdout);
    }

    return 0;
}

服务器进程终止

客户TCP收到FIN只是表示服务器进程已关闭连接的服务端,从而不再往其中发送任何数据而已。FIN的接收并没有告知客户TCP服务器进程已经终止。

SIGPIPE信号

当一个进程向某个已收到RST的套接字执行写操作时,内核向该进程发送一个SIGPIPE信号。该信号默认行为是终止进程。不论该进程捕获该信号并从其信号处理函数返回,还是简单的忽略该信号,写操作都将返回EPIPE。 第一次写操作引发RST,第二次写引发SIGPIPE信号。写一个已接收FIN的套接字不成问题,但写一个已接收了RST的套接字则是一个错误。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python小屋

Python使用UDP广播实现服务器自动发现

机房管理软件或者教学软件一般都由服务器和客户端组成,当客户端启动之后会自动搜索服务器,这是如何实现的呢?下面通过一段代码来演示其中一种比较高效的方法。 客户端...

3636
来自专栏Python攻城狮

UDP-用户数据报协议1.介绍2.udp网络程序-发送数据udp网络程序-发送、接收数据echo服务器广播用代码给飞秋发信息收消息_没绑定端口号收消息_绑定端口多线程聊天

UDP --- 用户数据报协议,是一个无连接的简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能...

1194
来自专栏抠抠空间

网络编程

阅读目录 一.楔子 二.客户端/服务端架构 三.网络基础 四.套接字(socket)初使用 五.黏包 六.验证客户端链接的合法性 七.socketserver ...

3136
来自专栏FreeBuf

SPN服务主体名称发现详解

Kerberos是一种支持票证身份验证的安全协议。如果客户端计算机身份验证请求包含有效的用户凭据和服务主体名称 (SPN),则 Kerberos 身份验证服务器...

1490
来自专栏.NET开发者社区

一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](三)

上一篇《一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](二)》我们通过如下操作:

23110
来自专栏向治洪

Android逆向分析(2) APK的打包与安装背后的故事

前言 上一次我们反编译了手Q,并遇到了Apktool反编译直接crash的问题,虽然笔者很想在这次解决这个问题,但在解决途中,发现该保护依赖于很多知识,所以本次...

3178
来自专栏有趣的Python

程序员装机必备爆款软件推荐与配置(windows版)

做机也要做一只全能的机哦 值此新年来临之即,面对两百多个G的c盘。忍痛割爱将电脑系统重装,版本为(win10:1079)之后的所有电脑环境更新,专业软件安装均会...

4083
来自专栏菩提树下的杨过

Silverlight调用本机exe程序

要点: 1. Silverlight必须启用OOB模式,以及 Require elevated trust when running in-browser.参考...

1988
来自专栏FreeBuf

技术分享:WIFI钓鱼的入门姿势

简介 该实验先是搭建一个测试环境,然后创建一个假的无线接入点,把网络连接连接到假的接入点并且强迫用户连接假的无线点。 事先准备 1.无线网卡:无线网卡用于数据包...

1929
来自专栏前端儿

【Chat】实验 -- 实现 C/C++下TCP, 服务器/客户端 "多人聊天室"

1352

扫码关注云+社区