socket编程TCP程序

目录

一.使用函数详解

1.listen函数

2.accept函数 

3.connect函数

4.read函数

5.write函数  

二.测试代码

1.单进程的TCP网络程序

2.多进程的TCP网络程序

3.多线程的TCP网络程序

4.线程池的TCP网络程序


 

一.使用函数详解

1.listen函数

函数:  int listen(int sockfd, int backlog);

参数:

  • sockfd:   需要设置为监听状态的套接字对应的文件描述符。
  • backlog:全连接队列的最大长度。如果有多个客户端同时发来连接请求,此时未被服务器处理的连接就会放入连接队列,该参数代表的就是这个全连接队列的最大长度,一般不要设置太大,设置为5或10即可。

返回值: 监听成功返回0,监听失败返回-1,同时错误码会被设置。

补充:
典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未accept的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接待状态,如果接收到更多的连接请求就忽略。

                 

2.accept函数 

函数:      int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:

  • sockfd:特定的监听套接字,表示从该监听套接字中获取连接。
  • addr:对端网络相关的属性信息,包括协议家族、IP地址、端口号等。
  • addrlen:调用时传入期望读取的addr结构体的长度,返回时代表实际读取到的addr结构体的长度,这是一个输入输出型参数。

返回值: 成功返回一个新的socket文件描述符,用于和客户端通信,失败返回-1,设置errno

(1)accept函数返回的套接字是什么?

调用accept函数获取连接时,是从监听套接字当中获取的。如果accept函数获取连接成功,此时会返回接收到的套接字对应的文件描述符。

(2)监听套接字与accept函数返回的套接字的作用:

  • 监听套接字:用于获取客户端发来的连接请求。accept函数会不断从监听套接字当中获取新连接。
  • accept函数返回的套接字:用于为本次accept获取到的连接提供服务。监听套接字的任务只是不断获取新连接,而真正为这些连接提供服务的套接字是accept函数返回的套接字。
     

                 

3.connect函数

函数:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数:

  • sockfd:特定的套接字,表示通过该套接字发起连接请求。
  • addr:对端网络相关的属性信息,包括协议家族、IP地址、端口号等。
  • addrlen:传入的addr结构体的长度。

返回值说明:连接或绑定成功返回0,连接失败返回-1,同时错误码会被设置。

                

4.read函数

函数:  ssize_t read(int fd, void *buf, size_t count);

参数:

  • fd:特定的文件描述符,表示从该文件描述符中读取数据。
  • buf:数据的存储位置,表示将读取到的数据存储到该位置。
  • count:数据的个数,表示从该文件描述符中读取数据的字节数。

返回值说明:

  • 如果返回值大于0,则表示本次实际读取到的字节个数。
  • 如果返回值等于0,则表示对端已经把连接关闭了。
  • 如果返回值小于0,则表示读取时遇到了错误。

                         

5.write函数

函数:  ssize_t write(int fd, const void *buf, size_t count);

参数:

  • fd:特定的文件描述符,表示将数据写入该文件描述符对应的套接字。
  • buf:需要写入的数据。
  • count:需要写入数据的字节个数。

返回值: 写入成功返回实际写入的字节数,写入失败返回-1,同时错误码会被设置。

        

                

                

二.测试代码

1.单进程的TCP网络程序

(1)代码

①Makefile

CC=g++

.PHONY:all
all:server client

server:tcp_server.cc 
		$(CC) -o $@ $^ -std=c++11

client:tcp_client.cc
		$(CC) -o $@ $^ -std=c++11

.PHONY:clean
clean:
		rm -f server client

②tcp_server.hpp

#pragma once 


#include
#include
#include 
#include 
#include 
#include 
#include 
#include 

#define DEFALUT 8080
#define BACKLOG 5   //全连接的最大数量

class TcpServer{
   private:
     int port;
     int lsock; //listen socket

   public:
     TcpServer(int _port = DEFALUT):port(_port),lsock(-1)
     {}
     ~TcpServer()
     {
       if(lsock >=0 )
         close(lsock);
     }

   public:
     void InitTcpServer()
     {
        lsock = socket(AF_INET,SOCK_STREAM,0);
        if(lsock < 0){
          std::cerr << "socket error!" << std::endl;
          exit(1);
        }

        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;

        if(bind(lsock,(struct sockaddr*)&local , sizeof(local)) < 0 ){
          std::cerr << "bind error!" << std::endl;
          exit(2);
        }


        //监听,等待链接到来
        if(listen(lsock,BACKLOG) < 0){
          std::cerr << "listen error!" << std::endl;
          exit(3);
        }
     }
      
     void Start()
     {
       //获取链接处理链接
       struct sockaddr_in peer;
       for(;;){
         socklen_t len = sizeof(peer);
         int sock = accept(lsock , (struct sockaddr*)&peer ,&len);
         if(sock < 0){
           std::cout << "accept error ,continue next" < 0){
             buffer[size] = 0;
             std::cout << ip << ":" << port << "# " << buffer << std::endl;

             write(sock,buffer,size);
          }
          else if(size == 0){
             std::cout << ip << ":" << port << "close!" << std::endl;
             break;
          }
          else{
             std::cerr << sock << " read error " << std::endl;
             break;
          }
       }

       close(sock); //fd也是一种资源是有限的
       std::cout << "service done!" << std::endl;
     }


};

1.TCP服务器在调用socket函数创建套接字时,参数设置如下:

  • 协议家族选择AF_INET,因为我们要进行的是网络通信。
  • 创建套接字时所需的服务类型应该是SOCK_STREAM,因为我们编写的是TCP服务器,SOCK_STREAM提供的就是一个有序的、可靠的、全双工的、基于连接的流式服务。
  • 协议类型默认设置为0即可。

                                 

2.TCP服务调用band函数

(1)套接字创建完毕后我们实际只是在系统层面上打开了一个文件,该文件还没有与网络关联起来,因此创建完套接字后我们还需要调用bind函数进行绑定操作。

(2)绑定的步骤如下:

  • 定义一个struct sockaddr_in结构体,将服务器网络相关的属性信息填充到该结构体当中,比如协议家族、IP地址、端口号等。
  • 填充服务器网络相关的属性信息时,协议家族对应就是AF_INET,端口号就是当前TCP服务器程序的端口号。在设置端口号时,需要调用htons函数将端口号由主机序列转为网络序列。
  • 在设置服务器的IP地址时,我们可以设置为本地环回127.0.0.1,表示本地通信。也可以设置为公网IP地址,表示网络通信。

你可能感兴趣的:(计算机网络,tcp/ip,服务器,网络,socket)