socket编程__TCP

 监听接口

监听接口:

        int listen(int sockfd, int backlog)

 sockfd:套接字描述符 , socket函数创建的侦听套接字 

 backlog : TCP并发连接数 (已完成连接的大小)

 tcp的服务端在一瞬间并发能够处理的最人的tcp连接数量
 强调: 并不是tcp服务端最多能够接收的tcp数量

 

 请求连接//这个函数是TCP客户端调用

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

sockfd:套接字描述符

addr : 地址信息结构,描述服务端地址信息(服务端的ip和端口)

addrlen : 地址信息长度

返回值:
      0:成功
      -1:失败

注意:该函数不仅仅可以完成连接功能,如果客户端没有绑定, 司时也会进行绑定客户端的地址信息

int accept(int sockfd,  struct sockaddr *addr,socklen t *addrlen);

sockfd : 套接字描述符侦听套接字
addr : 地址信息结构体,描述客户端地址信息的结构体
addrlen : 地址信息长度

返回值:
     成功: 返回新连接的套接字
     失败:-1;

注意: 这是个阻塞调用的函数

1.如果已完成队列当中没有已经建立连接的连接,则阻塞
2.如果有获取新连接之后, 就返回


返回的新连接的套接字,是为了和客户端进行通信的,只不过这个套接字没有进行监听功能,同时有客户端的地址信息

多个客户端发起连接, 在服务端会创建多个新连接的套接字

eg: 服务端使用socket创建的套接字描述符, 相当于是“拉皮条的”侦听套接字,
就在侦听是否有新连接到来相当于服务端使用accept创建出来的新连接套接字,
"接客"的小红,新连接套接字,就是在同客户端通信的
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

sockfd: 套接字描述符 (accept接受回来的新连接的套接字, 并非是侦听套接字!!!)

buf : 发送buf指向的空间的内容

len: 数据长度

flags : 0 (阻塞发送)

返回值:
返回发送的字节数量成功:
失败:-1
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

sockfd : 套接字描述符

   客户端调用recv:
   服务端调用recv:

   buf :将接受到的数据存放在buf指定的空间,空间需要程序猿提前开辟好
   len :期望接受的字节个数
   flags : 0(阻塞接收)
  

返回值:
成功: 接收到的字节数量
0 : 对端关闭连接了
-1:接收错误了
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>


int main(){
    /*1.创建套接字(流式套接字)*/
    int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sockfd < 0){
        perror("socket");
        return 0;
    }

    /*2.绑定地址信息, 服务端绑定ip和端口*/
    //int bind(int sockfd, const struct sockaddr *addr,
    //                                              socklen_t addrlen);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    //当前机器的私网ip地址(自己机器网卡的ip地址), 如果说要绑定, 则绑定该ip地址: 172.28.71.17
    //当前机器的公网IP地址(云服务器厂商为我们提供的ip地址, 从公网访问公网ip地址则能放到该机器):120.78.126.148
    //    !!!不要绑定公网ip地址, 因为公网IP地址不是当前机器的网卡地址
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("bind");
        return 0;
    }

    /*3.监听, 当调用了监听接口之后, 服务端就能接收客户端的连接请求了*/
    ret = listen(listen_sockfd, 2);
    if(ret < 0){
        perror("listen");
        return 0;

     while(1)
 {
sleep(1);

  }
}

 如果此时启动udp绑定的也是8080端口//可以启动成功吗

绑定端口也会区分udp端口和tcp端口
 
绑定端口冲突指的是同一个协议

 服务端与客户端通信(TCP)

客户端

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


int main(){
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        perror("socket");
        return 0;
    }

    //int connect(int sockfd, const struct sockaddr *addr,
    //                socklen_t addrlen);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = inet_addr("120.78.126.148");
    int ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("connect");
        return 0;
    }

    while(1){
        printf("please enter your msg: ");
        fflush(stdout);
        char buf[1024] = {0};
        std::cin >> buf;
        send(sockfd, buf, strlen(buf), 0);


        memset(buf, '\0', sizeof(buf));
        ssize_t r_size = recv(sockfd, buf, sizeof(buf) - 1, 0);
        if(r_size < 0){
            perror("recv");
            continue;
        }else if(r_size == 0){
            // server shutdown
            break;
        }else{
            printf("%s\n", buf);
        }
    }
    close(sockfd);
    return 0;
}

服务端

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


int main(){
    /*1.创建套接字(流式套接字)*/
    int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sockfd < 0){
        perror("socket");
        return 0;
    }

    /*2.绑定地址信息, 服务端绑定ip和端口*/
    //int bind(int sockfd, const struct sockaddr *addr,
    //                                              socklen_t addrlen);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    //当前机器的私网ip地址(自己机器网卡的ip地址), 如果说要绑定, 则绑定该ip地址: 172.28.71.17
    //当前机器的公网IP地址(云服务器厂商为我们提供的ip地址, 从公网访问公网ip地址则能放到该机器):120.78.126.148
    //    !!!不要绑定公网ip地址, 因为公网IP地址不是当前机器的网卡地址
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("bind");
        return 0;
    }

    /*3.监听, 当调用了监听接口之后, 服务端就能接收客户端的连接请求了*/
    ret = listen(listen_sockfd, 2);
    if(ret < 0){
        perror("listen");
        return 0;
    }

    while(1){
    /*4.接收新连接, 这个函数是阻塞调用的函数*/
    int new_sockfd = accept(listen_sockfd, NULL, NULL);
    if(new_sockfd < 0){
        perror("accept");
        return 0;
    }

        char buf[1024] = {0};
        ssize_t r_size = recv(new_sockfd, buf, sizeof(buf) - 1, 0);
        if(r_size < 0){
            perror("recv");
            continue;
        }else if(r_size == 0){
            // peer shutdown
            printf("client shutdown connection\n");
            close(new_sockfd);
            break;
        }else{
            printf("[buf is ] : %s\n", buf);
        }

        memset(buf, '\0', sizeof(buf));
        printf("please enter msg: ");
        fflush(stdout);
        std::cin >> buf;
        send(new_sockfd, buf, strlen(buf), 0);
    }
    
    close(listen_sockfd);
    return 0;
}

单线程的程序由于由于accept和recv都阻塞的特性,导致:单线程的程序,
如果accept和recv都进行循环调用,会导致accept阻塞影响recv的接收,
或者recv阻塞影响accept获取新连接并且:.accept接收回来的新连接套接
字B会覆盖之前的新连接套接字A

创建线程让工作线程去处理消息的收发

主线程创建工作线程与我们客户端进行消息收发

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

struct NewSockfd{
    int new_sockfd;
};


void* worker_thread(void* arg){
    pthread_detach(pthread_self());
    struct NewSockfd* ns = (struct NewSockfd*)arg;

    while(1){
        char buf[1024] = {0};
        ssize_t r_size = recv(ns->new_sockfd, buf, sizeof(buf) - 1, 0);
        if(r_size < 0){
            perror("recv");
            continue;
        }else if(r_size == 0){
            // peer shutdown
            printf("client shutdown connection\n");
            close(ns->new_sockfd);
            break;
        }else{
            printf("[buf is ] : %s\n", buf);
        }

        memset(buf, '\0', sizeof(buf));
        printf("please enter msg: ");
        fflush(stdout);
        std::cin >> buf;
        send(ns->new_sockfd, buf, strlen(buf), 0);

    }

    delete ns;
    return NULL;
}

int main(){
    /*1.创建套接字(流式套接字)*/
    int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sockfd < 0){
        perror("socket");
        return 0;
    }

    /*2.绑定地址信息, 服务端绑定ip和端口*/
    //int bind(int sockfd, const struct sockaddr *addr,
    //                                              socklen_t addrlen);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    //当前机器的私网ip地址(自己机器网卡的ip地址), 如果说要绑定, 则绑定该ip地址: 172.28.71.17
    //当前机器的公网IP地址(云服务器厂商为我们提供的ip地址, 从公网访问公网ip地址则能放到该机器):120.78.126.148
    //    !!!不要绑定公网ip地址, 因为公网IP地址不是当前机器的网卡地址
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("bind");
        return 0;
    }

    /*3.监听, 当调用了监听接口之后, 服务端就能接收客户端的连接请求了*/
    ret = listen(listen_sockfd, 2);
    if(ret < 0){
        perror("listen");
        return 0;
    }

    while(1){
        /*4.接收新连接, 这个函数是阻塞调用的函数*/
        int new_sockfd = accept(listen_sockfd, NULL, NULL);
        if(new_sockfd < 0){
            perror("accept");
            return 0;
        }

        struct NewSockfd * ns = new struct NewSockfd;
        ns->new_sockfd = new_sockfd;
        pthread_t tid;
        int ret = pthread_create(&tid, NULL, worker_thread, (void*)ns);
        if(ret < 0){
            close(new_sockfd);
            continue;
        }
    }

    close(listen_sockfd);
    return 0;
}
多线程处理方式:
       不能每个连接都创建一个线程,(后期线程切换是大问题)

创建子进程将进行业务处理

自定义SIGCHLD处理方式,父进程回收子进程状态信息

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

void sigcallback(int signo){
    wait(NULL);
}

int main(){
    signal(SIGCHLD, sigcallback);

    /*1.创建套接字(流式套接字)*/
    int listen_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sockfd < 0){
        perror("socket");
        return 0;
    }

    /*2.绑定地址信息, 服务端绑定ip和端口*/
    //int bind(int sockfd, const struct sockaddr *addr,
    //                                              socklen_t addrlen);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    //当前机器的私网ip地址(自己机器网卡的ip地址), 如果说要绑定, 则绑定该ip地址: 172.28.71.17
    //当前机器的公网IP地址(云服务器厂商为我们提供的ip地址, 从公网访问公网ip地址则能放到该机器):120.78.126.148
    //    !!!不要绑定公网ip地址, 因为公网IP地址不是当前机器的网卡地址
    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int ret = bind(listen_sockfd, (struct sockaddr*)&addr, sizeof(addr));
    if(ret < 0){
        perror("bind");
        return 0;
    }

    /*3.监听, 当调用了监听接口之后, 服务端就能接收客户端的连接请求了*/
    ret = listen(listen_sockfd, 2);
    if(ret < 0){
        perror("listen");
        return 0;
    }

    while(1){
        /*4.接收新连接, 这个函数是阻塞调用的函数*/
        int new_sockfd = accept(listen_sockfd, NULL, NULL);
        if(new_sockfd < 0){
            perror("accept");
            return 0;
        }

        int pid = fork();
        if(pid < 0){
            close(new_sockfd);
            continue;
        }else if(pid == 0){
            while(1){
                char buf[1024] = {0};
                ssize_t r_size = recv(new_sockfd, buf, sizeof(buf) - 1, 0);
                if(r_size < 0){
                    perror("recv");
                    continue;
                }else if(r_size == 0){
                    // peer shutdown
                    printf("client shutdown connection\n");
                    close(new_sockfd);
                    exit(1);
                }else{
                    printf("[buf is ] : %s\n", buf);
                }

                memset(buf, '\0', sizeof(buf));
                printf("please enter msg: ");
                fflush(stdout);
                std::cin >> buf;
                send(new_sockfd, buf, strlen(buf), 0);
            }
        }else{
            //wait(NULL);
        }

    }

    close(listen_sockfd);
    return 0;
}
多进程处理:

问题:每个连接创建一个子进程处理,大量连接会创建大量的子进程导致系统资源浪费严重

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java Socket编程是一种网络编程模型,它是建立在TCP/IP协议上的,允许在网络上进行数据通信。Java Socket编程提供了一种方便的方法,可以通过网络连接来传输数据。 以下是Java Socket编程的一些基本步骤: 1. 创建一个ServerSocket对象,指定端口号,并监听客户端请求。 2. 当客户端请求连接时,ServerSocket会接收请求,并创建一个Socket对象,用于与客户端进行通信。 3. 使用Socket对象的输入输出流来进行数据传输。 4. 客户端与服务端通信完成后,关闭Socket对象和ServerSocket对象。 下面是一个简单的Java Socket编程示例,用于创建一个简单的服务端: ```java import java.net.*; import java.io.*; public class ServerSocketDemo { public static void main(String[] args) throws IOException { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(8088); System.out.println("Server started."); while (true) { Socket socket = serverSocket.accept(); System.out.println("Client connected."); // 从客户端读取数据 InputStream inputStream = socket.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String data = reader.readLine(); System.out.println("Received data from client: " + data); // 向客户端发送数据 OutputStream outputStream = socket.getOutputStream(); PrintWriter writer = new PrintWriter(outputStream); writer.println("Hello, client!"); writer.flush(); // 关闭Socket对象 socket.close(); } } catch (IOException e) { System.out.println("Error: " + e.getMessage()); } finally { if (serverSocket != null) { serverSocket.close(); } } } } ``` 在上面的示例中,我们创建了一个ServerSocket对象,并指定端口号为8088。然后,我们使用while循环来监听客户端请求。当有客户端请求连接时,我们使用serverSocket.accept()方法来接收请求,并创建一个Socket对象,用于与客户端通信。接着,我们使用Socket对象的输入输出流来进行数据传输。最后,我们关闭Socket对象和ServerSocket对象。 这只是一个简单的示例,Java Socket编程还有很多其他用法和技巧。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值