TCP 网络编程 TCP C/S 架构 socket connect send recv bind listen accept 三次握手 四次挥手 多进程实现并发

TCP 介绍、编程流程

  TCP 回顾

  •         面向连接的流式协议;
  •        可靠,出错重传,且每收到一个数据给相应的确认
  •        通信之前建立链接
    •        服务器被动链接,客户端主动链接

 

 TCP 与 UDP 的差异
 

TCP C/S 架构
 


TCP 编程-socket

    TCP 套接字创建


          UDP 套接字创建回顾
 

int sockfd;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
    perror("socket");
    exit(-1);
}

         创建 TCP 套接字
 

int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
    perror("socket");
    exit(-1);
}

    做为客户端需要具备的条件

  •        知道“服务器”的ip,port
  •        主动连接“服务器”
  •        用到的函数

                   socket      创建“主动TCP套接字”

                  connect      连接“服务器”

                  send          发送数据到“服务器”

                  recv            接受“服务器”的响应

                  close        关闭连接


TCP 客户端-connect、send、recv

    connect 函数

/*
 *function:
 *  主动跟服务器建立链接
 *parameter:
 *  sockfd:socket套接字
 *  addr:  连接的服务器地址结构
 *  len:   地址结构体长度
 *return:
 *  成功:0
 *  失败:其他
 *note:
 *  connect建立连接之后不会产生新的套接字
 *  连接成功后才可以开始传输TCP数据  
*/
#include <sys/socket.h>

int connect(int sockfd, sonst struct sockadr *addr, socklen_t len);

  send 函数
 

/*
 *function:
 *  发送数据
 *parameter:
 *  sockfd:  已建立连接的套接字
 *  buf:     发送数据的地址
 *  nbytes:  发送缓数据的大小(字节为单位)
 *  flags:   套接字标志(常为0)
 *return:
 *  成功:发送的字节数
 *note:
 *  不能用TCP协议发送 0 长度的数据包
 */
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);

recv 函数
 

/*
 *function:
 *  接收网络数据
 *paratemer:
 *  sockfd:   套接字
 *  buf:       接收网络数据的缓冲区的地址
 *  nbytes:    接收缓冲区的大小(字节为单位)
 *  flags:     套接字标志(常为0)
 *return:
 *  成功:接收到字节数
 */
#include <sys/socket.h>

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

 

客户端 code

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

int main(int argc, char *argv[])
{
    unsigned short port = 8000;         //服务器的端口号
    char *server_ip = "172.20.226.11";    //服务器的IP

    //给main传参
    if(argc > 1)                        //函数参数,可以改服务器IP
    {
        server_ip = argv[1];
    }
    if(argc > 2)                        //函数参数,可以改服务器端口号
    {
        port = atoi(argv[2]);
    }

    //创建TCP套接字
    int sockfd = 0;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);  //创建通行端点:套接字
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }
    
    //设置连接的IP,端口
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));    //初始化服务器的地址
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    inet_pton(AF_INET, server_ip, &server_addr.sin_addr);
    
    //连接服务器
    int err_log = connect(sockfd, (struct sockaddr *)&server_addr,sizeof(server_addr));
    if(err_log != 0)
    {
        perror("connect");
        close(sockfd);
        exit(-1);
    }
     
    char send_buf[512] = "";
    char recv_buf[512] = "";
    printf("send data to %s:%d \n", server_ip,port);
    
    //发送消息
    printf("send");
    fgets(send_buf, sizeof(send_buf), stdin);
    send_buf[strlen(send_buf) - 1] = 0;            //除去‘\n’
    send(sockfd, send_buf, strlen(send_buf), 0);   //向服务器发送数据
        
    //接收数据
    recv(sockfd, recv_buf, sizeof(recv_buf), 0);  //接受服务器的响应
    printf("recv: %s \n",recv_buf);
    close(sockfd);

}



TCP 服务器-bind、listen、accept

     做为 TCP 服务器需要具备的条件

  •       有一个确定的地址
  •        操作系统知道一个服务器
  •       等待连接的到来

   对于面向连接TCP协议来说,连接的建立才是数据通信的开始


      bind 示例

int err_log = 0;
unsigned short port = 8000;
struct sockaddr_in my_addr;

bzero(&muy_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);

err_log = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
if(err_log != 0)
{
    perror("binding");
    close(sockfd);
    exit(-1);
}

      listen 函数

/*
功能:
    套接字由主动改为被动
    操作系统为套接字设置一个连续队列,用来记录所有连接到该套接字的连接

参数:
    sockfd:socket监听套接字
    backlog:连接队列的长度
 
返回值:
    成功:返回 0
    失败:其他
*/
#include <sys/socket.h>

int listen(int sockfd, int backlog);

     accept 函数

/*
功能:
    从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待(阻塞)

参数:
    sockfd:socket 监听套接字
    cliaddr:用于存放客户端套接字地址结构
    addrlen:套接字地址结构体长度的地址

返回值:
    已连接套接字

注意:
    返回的是一个已连接套接字,该套接字代表当前这个连接

*/
#include <sys/socket.h>

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

      TCP 服务器例子
 

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

int main(int argc, char *argv[])
{
    unsigned short port = 8000;
    if(argc > 1)
    {
        port = atoi(argv[1]);
    }
    //创建TCP套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        perror("socket");
        exit(-1);
    }
    //组织本地信息
    struct sockaddr_in my_addr;
    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(port);
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //绑定信息
    int err_log = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
    if(err_log != 0)
    {
        perror("binding");
        close(sockfd);
        exit(-1);
    }
    //“主动”变“被动”
    err_log = listen(sockfd, 10);
    if(err_log != 0)
    {
        perror("listen");
        close(sockfd);
        exit(-1);
    }
    printf("listen client &poort = %d...\n",port);
    while(1)
    {
        struct sockaddr_in client_addr;
        char cli_ip[INET_ADDRSTRLEN] ="";
        socklen_t cliaddr_len sizeof(client_addr);
        
        int connfd;
        //等待连接的到来
        connfd = accept(sockfd, (struct sockaddr *)&client_addr, &cliaddr_len);
        if(connfd < 0)
        {
            perror("accept");
            continue;
        }
        //转换并打印信息
        inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
        printf("--------------------------------\n");
        printf("client ip = %s, port = %d\n",cli_ip, ntohs(client_addr.sin_port));

        char recv_buf[2048] = "";
        //接收消息
        while(recv(connfd, recv_buf, sizeof(recv_buf), 0) > 0)
        {
            printf("\n recv data: \n");
            printf("%s\n",recv_buf);
        }
        
        close(connfd);        //关闭已连接的套接字
        printf("client closed \n");
    }
    close(sockfd);        //关闭监听的套接字
    return 0;
}


TCP 编程-close、三次握手、四次挥手

   close 关闭套接字

  •        使用close函数关闭套接字

                 关闭一个代表已连接套接字将导致另一端接收到一个0长度的数据包

  •        做服务器时

                    1      关闭监听套接字将导致服务器无法接收新的连接,但不影响已建立的连接

                    2     关闭accept返回已连接套接字将导致它代表的连接被关闭,但不会影响服务器的监听

  •        做客户端时

                 关闭连接就是关闭连接,不意味着其他

     三次握手

 

     四次挥手



TCP 并发服务器

     多进程实现并发

//#include <头文件>

int main(int argc, char *argv[])
{
    //创建套接字sockfd
    //绑定(bind)套接字sockfd
    //监听(listen)套接字sockfd
    
    while(1)
    {
        int connfd = accept();
        
        if(fork() == 0)        //子进程
        {
            close(sockfd);    //关闭监听套接字sockfd
            
            fun();            //服务客户端的具体事件在fun里实现
            close(connfd);    //关闭已连接套接字connfd
            exit(0);          //结束子进程
        }
        
        close(connfd);        //关闭已连接套接字connfd
    }
    close(sockfd);
    return 0;
}

 

  多线程实现并发

//#include<头文件>

int main(int argc, char *argv[])
{
    //创建套接字sockfd
    //绑定(bind)套接字sockfd
    //监听(listen)套接字sockfd

    while(1)
    {
        int connfd = accept();
        pthread_t tid;
        pthread_create(&tid, NULL, (void *)client_fun, (void *)connfd);
        pthread_detach(tid);
    }

    close(sockfd);        //关闭监听套接字
    
    return 0;
}

void *client_fun(void *arg)
{
    int connfd = (int) arg;
    fun();                //服务与于客户端的具体程序
    close(connfd);
}


Web 服务器介绍

    web 服务器简介

         web服务器:

                www服务器,网站服务器

           特点:

                  使用HTTP协议与客户机浏览器进行信息交流

                 不仅存储信息,还可以在用户通过web浏览器提供的信息的基础上运行脚本和程序

                 该服务器可安装在UNIX,Linux,Windows等操作系统上

                著名的服务器有Apache,Tomcat,IIS

       HTTP 协议

             webserver    HTTP协议

                概念

                   一种详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议

               特点

  •            支持C/S架构
  •            简单快速:客户向服务器请求服务时,只传送请求方法和路径,常用GET,POST
  •            无连接:限制每次连接只处理一个请求
  •            无状态:如果后续处理需要前面的信息,它必须重传,这样可能导致每次连接传送的数据量增大

 

    Webserver 通信过程
 

 

 


 

全部评论

相关推荐

10-05 23:02
东北大学 Java
我说句实话啊:那时候看三个月培训班视频,随便做个项目背点八股,都能说3 40w是侮辱价
点赞 评论 收藏
分享
点赞 1 评论
分享
牛客网
牛客企业服务