socket需要关闭吗_一个简单的多线程socket通信

628ed2e0a0659438773acf3ed5b2a1a9.png

程序主要分为两部分: 套接字通讯的建立 和 线程(用thread)函数的编写和使用

主要是这两个东西都比较陌生,网上好多博客细节都不是很到位,中间纠结了好久。

程序主要流程:WSA初始化(win套接字环境)->初始化套接字->服务器绑定并监听->服务器接收客户端->创建一个thread与这个客户端通信,在任务函数里死循环接收和发送->继续等待接收 (客户端把绑定监听和接收变成 主动连接就行了)。

这里附上两段代码,一段是服务器的main,一段是服务器的运行函数:

int main()                                         //main内即主线程
{
    if(!WSA_init() || !Creat_Socket(sServer)||!Bind_A_Listen())// 初始化
      return-1;
    while(1)
    {
     if(Service_Run())              //服务器运行
     Sleep(1000);
     else {
         printf("Sth wrong,bye...n");
         break;}
    }
  WSACleanup();
  system("pause");
}
//
bool Service_Run()
{
    SOCKADDR_IN AcptClitAddr;
    int len = sizeof(SOCKADDR);//TODO 零时变量怎么让子线程保存 赋值产生的socket需要关闭吗

    sSrReSen[++ct]=accept(sServer,(SOCKADDR*)&AcptClitAddr,&len);
    sockAcpt=sSrReSen[ct];
    if(INVALID_SOCKET == sockAcpt)
     {printf("Accept socket error:  %dn",WSAGetLastError()); return false;}
    else  printf("Accept client IP:[%s]n", inet_ntoa(AcptClitAddr.sin_addr));  //错误打印错误信息,成功打印IP

    //现在已经成功建立了链接,该使用分线程执行任务了
    if(!Pthread_SET(120,PTHREAD_CREATE_DETACHED,Thread_Work,(void*)&ct))
    return false;

    return true;
}

其中thread的任务函数Threadz_Work() 主要都是字符串打印,注意一下子线程里静态变量和全局变量的使用就可以了,后面会提到.

最后的效果:时间是自己打印的。

1ef1dd22c433f99afff6978c9980c12a.png

我遇到的一些迷惑点:

1.重复调用回调函数时,其中的静态声明会串吗?

A:会串,看来静态变量和全局变量一样,不会储存在独立的堆栈里,但能不用尽量不用,要加锁容易出错。

堆与栈的区别_Dablelv的博客专栏-CSDN博客

2.赋值产生的SOCKET,需要手动关闭吗?

A:不用,SOCKET 本质上是unsigned longlong 储存的是句柄(也就是相当于只储存了地址,没储存内容信息,所以SOCKET的复制就和int复制一样,不用担心深拷贝之类的),只要销毁一一次,其他的SOCKET虽然有值,但已经无法操作了。

注意,句柄相当于是一个整数类型的指针,但跨越编译器和语言,操作系统级的,这是为了更好的使用一些通用功能(窗口、socket之类)。

C++ 什么是句柄?为什么会有句柄?HANDLE

3.在函数中声明的 SOCKET 传入独自循环的回调函数后,当函数运行结束,这个SOCKET还存在吗

A:存在,SOCKET没有储存内容信息,只是传递了句柄,因此SOCKET类型的变量的死活不会影响建立的SOCKET链接(就是地址和变量的关系),这个SOCKET本身不会消失,这是丢了指向的内容。

4.每个线程中都有延时sleep,甚至阻塞语句 accept recive 等时,他们是如何工作的。

A:之前写过一些嵌入式,里面用了UCOS系统,就是当程序有多个任务是,自动协调先后顺序,类似于见缝插针,本质上还是只有一个主线程。而thread各个线程都是完全独立并行的,并不是像嵌入式的操作系统那样间歇性交替,只要注意不要让它们抢夺调用资源形成锁就行。(即便对于单核的CPU来说本质上是串行的,但是底层会自动协调,不用自己考虑这个线程等待的时候那个线程要不要执行这种事情)

注意:sleep这种硬延时还是应该少用,C++里有可以获得线程执行状态的函数,可以减少资源浪费。

5.函数指针,回调函数与void* :void *(*workfun)(void *)?

A:

  • void* 类型为void的指针,“void*能包容地接受各种类型的指针。也就是说,如果你期望接口能够接受任何类型的参数,你可以使用void*类型。但是在具体使用的时候,你必须转换为具体的指针类型。”(知乎),就好比寄快递,知道发货地址和收获地址,void*就像运货的,不管你给啥都给你传过去,但你要知道自己寄的是啥东西,送到那干嘛。
  • workfun做为回调函数,一般难以确定传入和传处参数,故返回值和参数都是void*
  • (*workfun)是函数指针定义时的表示方式,一定要加括号,前面的是返回类型,后面的是参数列表,函数指针定义必须两个都要有。

6.SOCKADDR_IN 的 协议族、端口和IP地址,为啥都一样都不会串?

A:虽然网上一会说可以一样一会说不能一样,但二者的究极目的就是为了区分链接的客户端,然而本程序用的client监听和accept函数获取的客户端,也就是说每次链接成果后都会得到指定的对应本次链接的SOCKET句柄,因此只要把它们分开保存好,服务器的端口和IP可以随便设置...(对于其他模式和工作方式应该端口和IP是有用的,我对TCP传输什么的还不太了解,这里TODO)。

注意:区分不同客户端的方法还有很多,比如两边按照一定的通讯协议进行身份验证。

剩下就是一些看上去令人迷惑的宏定义,多百度百度就好了

这里是完整的代码:

readme.txt
847
·
百度网盘
sktMultiThred.h
510
·
百度网盘
SktMultiThredF.cpp
4K
·
百度网盘
SktMultiThred_Client.cpp
1.7K
·
百度网盘
SktMultiThred_Client.exe
133.8K
·
百度网盘
SktMultiThred_Server.cpp
914
·
百度网盘
SktMultiThred_Server.exe
108K
·
百度网盘
socket.h
910
·
百度网盘
socketf.cpp
2.5K
·
百度网盘

最后推荐俩博客 虽然不全但写的比较好:

Linux Socket编程(不限Linux)​www.cnblogs.com
16672a53c9042b49b9da871cf6b49d35.png
网络socket编程实现并发服务器--多线程编程_Mastema-CSDN博客​blog.csdn.net
8dbec974f4bf35f9f845f87f9a05977f.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值