Linux网络编程(2)

一.网络基础知识

1. TCP状态转换图

在这里插入图片描述主动连接端:CLOSED --> SYN_SENT --> ESTABLISHED
被动连接端:CLOSED --> LISTEN --> SYN_RCVD --> ESTABLISHED
主动关闭端:ESTABLISHED --> FIN_WAIT_1 --> FIN_WAIT_2(半关闭)–> TIME_WAIT [2MSL超时]–> CLOSED
被动关闭端:ESTABLISHED --> CLOSE_WAIT(对端处于半关闭) --> LAST_ACK -->CLOSED

2MSL:确保最后ACK被成功接收

查询状态: netsata -apn

2.C/S,B/S模型

browser-server(浏览器)
client-server(客户端)

类型C/SB/S
优点缓存大量数据,可自定义协议, 效率高速度快安全性相对低,不能跨平台,开发工作量相当较大
缺点安全性相对高,跨平台,开发工作量相对较小不能缓存大量数据,严格遵守http协议

3.端口复用

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

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

int opt = 1;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

二.并发服务器

1.多进程

父进程用来监听客户端的连接请求,每当一个客户端发来连接请求时父进程为其创建一个进程和该客户端进行某些操作以达到并发效果。

1.基本流程

socket,bind,listen --> accept --> fork
父进程 --> signal/sigaction --> accept
子进程 -->read -->write

注意:1.父子进程都要关闭相对的描述符再进行操作
2.如果父进程调用wait回收子进程时父进程会阻塞等待,所以调用waitpid, 参数option填宏WNOHANG,使得父进程不阻塞在回收上,但需要子进程结束的信号(SIGCHLD)来通知父进程回收

2.简单应用

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

void catch_child(int signum)
{
    while((waitpid(0, NULL,WNOHANG))>0);

    return ;
}

int main()
{
    struct sockaddr_in caddr, saddr;
    int cfd, sfd;
    socklen_t clen = sizeof(caddr);
    pid_t pid;
    int ret;
    char buf[BUFSIZ], str[INET_ADDRSTRLEN];;

    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    sfd = socket(AF_INET, SOCK_STREAM, 0);
    
    bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr));

    listen(sfd, 128);

    while(1)
    {
        cfd = accept(sfd, (struct sockaddr *)&caddr, &clen);
        printf("connection -- port:%d\tip:%s\n", ntohs(caddr.sin_port), inet_ntop(AF_INET, &caddr.sin_addr.s_addr, str, sizeof(str)));
        pid = fork();
        switch (pid){
            case 0:
                close(sfd);
                while(1)
                {
                    ret = read(cfd, buf, sizeof(buf));
                    if(ret == 0)
                    {
                        printf("connection terminated.\n");
                        break;
                    }
                    else
                    {
                        for(int i=0; i<ret; i++) 
                            buf[i] = toupper(buf[i]);
                        write(cfd, buf, ret);
                        write(STDOUT_FILENO, buf, ret);
                    }
                }
                break;
            case -1:
                perror("fork");
                exit(1);
            default:
            {    
                struct sigaction act;
                act.sa_handler = catch_child;
                sigemptyset(&act.sa_mask);
                act.sa_flags = 0;
                
                sigaction(SIGCHLD, &act, NULL);
                close(cfd);
                continue;
            }
        }
    }
    return 0;
}

2.多线程

跟多进程并发服务器类似,主线程用来监听客户端连接请求,每当一个客户端发出连接请求时主线程创建一个副线程和该客户端进行某些操作以达到并发效果。

1.基本流程

socket,bind,listen --> accept --> pthread_create
主线程 --> pthread_detach --> accept
副线程 --> read --> write

注意:因为直接pthread_join回收副线程资源时会阻塞主线程,所以通过pthread_detach分离副线程,等副线程结束时自动释放资源而不使用pthread_join

2.简单应用

#include <stdio.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>

void *obj(void *arg)
{
    int n, j;
    int *cfd = (int*)arg;
    char buf[BUFSIZ];
    while(1)
    {
        n = read(*cfd, buf, sizeof(buf));
        if(n == 0)
        {
            printf("connection terminated.\n");
            break;
        }
        else
        {
            for(j = 0; j < n; j++)
                buf[j] = toupper(buf[j]);
            write(STDOUT_FILENO, buf, n);
            write(*cfd, buf, n);
        }
    }
    close(*cfd);
    return (void *)0;
}

int main()
{
    int lfd, cfd;
    socklen_t clen;
    char str[INET_ADDRSTRLEN];
    struct sockaddr_in caddr, saddr;

    bzero(&saddr, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(7777);
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);

    lfd = socket(AF_INET, SOCK_STREAM, 0);
    bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
    listen(lfd, 128);

    pthread_t tid;
    while(1)
    {
        clen = sizeof(caddr);
        cfd = accept(lfd, (struct sockaddr *)&caddr, &clen);
        printf("connection -- port:%d\tip:%s\n", ntohs(caddr.sin_port), inet_ntop(AF_INET, &caddr.sin_addr.s_addr, str, sizeof(str)));
        pthread_create(&tid, NULL, obj, (void *)&cfd);
        pthread_detach(tid);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值