Tcp socket程序编写

TCP网络程序编写

套接字的类型:

1. 流式套接字(SOCK_STREAM)

   提供面向连接,可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接收

 

2. 数据报式套接字(SOCK_DGRAM)

   提供无连接服务,数据包以独立包形式发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱

3. 原始套接字(sock_raw)

 

基于TCP(面向连接)的socket编程

服务器端程序:

1. 创建套接字(socket)

2. 将套接字绑定到一个本地地址和端口上(bind)

3. 将套接字设为监听模式,准备接收客户请求(listen)

4. 等待客户请求到来;当请求到来后, 接受连接请求,返回一个新的对应于此次连接的套接字(accept)

5. 用返回的套接字和客户端进行通信(send/recv)

6. 返回, 等待另一客户请求

7. 关闭套接字

 

客户端程序:

1.创建套接字(socket)

2。向服务器发出连接请求(connect)

3。和服务器端进行通信(send/recv)

4。关闭套接字.

 

 

 

 

A2 基于TCP的Socket连接步骤

控制台TCP服务器端接收发送消息

#include <Winsock2.h>

#include <stdio.h>

void main()

{

       WORD wVersionRequested;

       WSADATA wsaData;

       int err;

       wVersionRequested = MAKEWORD( 1, 1 );//版本号1.1

       //1.加载套接字库     

       err = WSAStartup( wVersionRequested, &wsaData );

       if ( err != 0 )

      {

              return;   

    }  

      //判断是否我们请求的winsocket版本,如果不是清除SOCKET后退出

       if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )

       {

              WSACleanup( );

              return;

       }

       //2.创建套接字

       SOCKET sockSrv = socket(AF_INET,

                                  SOCK_STREAM,//创建流式套接字

                                  0);         //零表示自动选择协议

       //3.将套接字绑定到本地地址与端口上

       SOCKADDR_IN addrSrv;

       addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

       //htonl()方法将INADDR_ANY转换为网络字节序

       addrSrv.sin_family = AF_INET;

       addrSrv.sin_port = htons(6000);

       //htonsu_short类型从主机字节序转换为网络字节序

       bind(sockSrv,(SOCKADDR *) &addrSrv,sizeof(SOCKADDR));

       //SOCKADDR大小写是一样的

       //4.设置监听,5是最大监听数

listen(sockSrv,5);

       SOCKADDR_IN addrClient;

       int len = sizeof(SOCKADDR);

       while(1)

       {    

SOCKET sockConn = accept(sockSrv,(SOCKADDR *) &addrClient,&len);

       char sendBuf[100];

       sprintf(sendBuf,"Welcome %syuliqi",inet_ntoa(addrClient.sin_addr));

       send(sockConn,sendBuf,strlen(sendBuf)+1,0);

       char recvBuf[100];

       recv(sockConn,recvBuf,100,0);

       printf("%s/n",recvBuf);

       closesocket(sockConn);

       }

}

控制台TCP客户端接收发送消息

#include <Winsock2.h>

#include <stdio.h>

void main()

{

       WORD wVersionRequested;

       WSADATA wsaData;

       int err;

       wVersionRequested = MAKEWORD( 1, 1 );//版本号1.1

       //1.加载套接字库     

       err = WSAStartup( wVersionRequested, &wsaData );

       if ( err != 0 )

       {

              return;

       }

       //判断是否我们请求的winsocket版本,如果不是清除SOCKET后退出

       if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )

       {

              WSACleanup( );

              return;

       }

       //2.创建套接字

       SOCKET sockClient = socket (AF_INET, SOCK_STREAM, 0);

       //3.连接服务器端,无须绑定

       SOCKADDR_IN addrSrv;

       addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//设定服务器端IP地址,

       addrSrv.sin_family = AF_INET;

       addrSrv.sin_port = htons(6000); //端口号要与服务器端保持一致

       //4.接收服务器端发送的数据,并且向服务器端发送数据

       connect(sockClient,(SOCKADDR *)&addrSrv,sizeof(SOCKADDR));

       char recvBuf[100];

       recv(sockClient,recvBuf,100,0);

       printf("%s /n", recvBuf);

//发送信息

       send(sockClient,"I'm teshorse",strlen("I'm teshorse")+1,0);

       //5.关闭套接字,释放资源,并且终止对套接字库的使用

       closesocket(sockClient);

       WSACleanup();

}

阻塞模式下的套接字会因为等待消息而是进程处于挂起状态应当,使用多线程可以解决这个问题

多线程TCP客户端接收发送消息

1.APPInitInstance中初始化套接字,需要加入Afxsock.h头文件

BOOL CChatApp::InitInstance()

{

       if(!AfxSocketInit())//加载套接字库,进行版本协商

       {

              AfxMessageBox("加载套接字失败!");

              return FALSE;

       }

}

2.定义结构体, 通过LPVOID类型的指针传递到进程中

     struct RECVPARAM

     {

            SOCKET sock;//连接的socket

            HWND hwnd;//发送消息的句柄

     };

3.定义成员变量套接字SOCKET m_socket;

4.定义初始化套接字的函数InitSocket()

BOOL CThreadchatDlg::InitSocket()

{

     //初始化套接字:

     m_socket = socket(AF_INET,SOCK_DGRAM,0);

     if(INVALID_SOCKET == m_socket)

     {

            AfxMessageBox("套接字创建失败");

            return FALSE;

     }

     SOCKADDR_IN addrSock;

     addrSock.sin_family=AF_INET;

     addrSock.sin_port=htons(6000);

     addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);

     //INADDR_ANY表示接收任意IP地址

     int retval;

     //服务器端绑定

     retval=bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));

     if(SOCKET_ERROR==retval)

     {

            closesocket(m_socket);

            AfxMessageBox("绑定失败");

            return FALSE;

     }

     return TRUE;

}

 

OnInitDialog()函数中添加如下代码:

     InitSocket(); //初始化套接字

     RECVPARAM *pRecvParam=new RECVPARAM;//用来传递参数的结构体

     pRecvParam->hwnd=m_hWnd;

     pRecvParam->sock=m_socket;

     HANDLE hThread=CreateThread(NULL,0,RecvProc/*线程函数*/,

                          (LPVOID)pRecvParam,0,NULL);//创建线程

     CloseHandle(hThread);

//关闭线程句柄,这样当线程结束时,线程内核对象被释放,否则只有当进程结束,才释放线程的内核对象

//线程回调函数要定义成static成员函数

DWORD WINAPI CChatDlg::RecvProc(LPVOID lpParameter)

{

     SOCKET sock=((RECVPARAM*)lpParameter)->sock;

     HWND hwnd=((RECVPARAM*)lpParameter)->hwnd;

     SOCKADDR_IN addrFrom;

     int len=sizeof(SOCKADDR);

     char recvBuf[200];

     char tempBuf[400];

     int retval;

     while(TRUE)

     {

            retval=recvfrom(sock,recvBuf,200,NULL,

                   (SOCKADDR*)&addrFrom,&len);

            if(SOCKET_ERROR==retval)

                   break;

            sprintf(tempBuf,"%s said:%s",

                   inet_ntoa(addrFrom.sin_addr),recvBuf);

            ::PostMessage(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);

            //通过发送消息方式把数据传给对话框,

            //传递一个自定义消息WM_RECVDATA

            //将数据通过lParam进行传递。

     }

     return 0;

}

定义消息WM_RECVDATA实现向接收框传递数据

     void CChatDlg::OnRecvData(WPARAM wParam,LPARAM lParam)

     {

            CString str=(char*)lParam;

            CString strTemp;

            GetDlgItemText(IDC_EDIT_RECV,strTemp);

     //     str+="/r/n";//换行需要变更编辑框的属性  

 

            str=strTemp+"/r/n"+str;

            SetDlgItemText(IDC_EDIT_RECV,str);

     }

接收消息的代码完成,下面完成的是发送消息的代码

void CThreadchatDlg::Onsendmsg()

{

       //1.首先要得到IP地址

       DWORD dwIP;

       ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);

       //2.定义地址结构体变量

       SOCKADDR_IN addrTo;

       addrTo.sin_addr.S_un.S_addr=htonl(dwIP);

       addrTo.sin_family=AF_INET;

       addrTo.sin_port=htons(6000);

       //3.得到发送编辑框口要发送的数据

       CString strSend;

       GetDlgItemText(IDC_EDIT_SEND,strSend);

       //4.发送数据

       sendto(m_socket,strSend,strSend.GetLength()+1,0,

              (SOCKADDR*)&addrTo,sizeof(SOCKADDR));

       SetDlgItemText(IDC_EDIT_SEND,"");

       //把发送框设为空白

}

你可能感兴趣的:(多线程,socket,tcp,Stream,服务器,winapi)