Socket通信程序设计-网络实验4

3.实验指导

(1)什么是Socket

     Socket(套接字)是当前最流行的网络编程接口(网络编程的API)。Socket使用网络层的IP地址和传输层的端口号进行数据交换

(2) 常用Socket类型

  1、流式套接字(SOCK_STREAM),基于TCP协议,提供面向连接的、可靠的数据传输服务

  2、数据报套接字(SOCK_DGRAM),基于UDP协议,提供无连接的、不可靠的数据传输服务

  3、原始套接字(SOCK_RAW),提供对网络层协议的访问,可以保存IP数据包中完整的头部,前两者不保留,只是存储或转发

(3) TCP编程模式

(4) Server端程序设计要点

①相关对象的声明;

②启动监听;

③接受Client端连接请求,建立与Client端的连接;

④流式Socket上的数据接收;

⑤流式Socket上的数据发送;

⑥释放资源.

(5) Client端程序设计要点   Client端的程序设计与Server端有许多相似之处。差异主要在建立连接时,服务器端处于监听状态,而客户端要发起连接请求.一旦连接建立后,双方就将平等地发送与接收数据.

•根据代码注释,读懂示例代码,自行模仿设计Server端和Client端,要求能运用SOCK_STREAM,实现使用端口8888的Server端和Client端,能够进行最简单的聊天功能,你一句我一句交互即可。
参考与资料扩展:
•代码中注释
•谷歌搜索
•MSDN

客户端代码:

#include "stdafx.h"
#include "winsock2.h"
#pragma comment(lib,"ws2_32.lib")

#define BUFFSIZE    80

int _tmain(int argc, _TCHAR* argv[])
{
    WORD        wVersionRequested;
    WSADATA        wsaData;
    SOCKET        sockfd;    
    SOCKADDR_IN servAddr;

    wVersionRequested = MAKEWORD(2,2);    
    
    if (WSAStartup(wVersionRequested,&wsaData) != 0)
    {
        printf("Winsock init fail!\n");
        return (-1);        
    }

    sockfd = socket(AF_INET,SOCK_STREAM,0);
    if (sockfd == INVALID_SOCKET) {
        printf("Create socket fail!\n");
        WSACleanup();
        return (-1);
    }
    
    
    servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(6000);
    
    // 向服务器发送连接请求
    if(connect(sockfd, (SOCKADDR*)&servAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) 
    {
        printf("Connect fail!\n");
        return (-1);
    } else 
        printf("Connect success!\n");

    // 和服务器进行通信        
    char    buffSend[BUFFSIZE], buffRecv[BUFFSIZE];
    int        count;

    while (true)
    {
        printf("Send: ");
        gets(buffSend);

        if((send(sockfd, buffSend, strlen(buffSend), 0)) == SOCKET_ERROR)
        {
            printf("Send fail!\n");
            return (-1);
        }

        if((count = recv(sockfd, buffRecv, BUFFSIZE, 0)) == SOCKET_ERROR)
        {
            printf("Recv fail!\n");
            return (-1);
        }

        buffRecv[count] = '\0';

        printf("Received :[%d bytes] %s  \n",  count, buffRecv);

        if(!strcmp(buffRecv, "88")) 
            break;
        
    }

    if (closesocket(sockfd) == SOCKET_ERROR)
    {
            printf("Close fail!\n");
            return (-1);
    }
    WSACleanup();    

    getchar();
    return 0;
}

服务器代码:

#include "stdafx.h"
#include "winsock2.h"
#pragma comment(lib,"ws2_32.lib")    // 连接socket所需的库文件

#define BUFFSIZE    80

int _tmain(int argc, _TCHAR* argv[])
{
    WORD        wVersionRequested;        // 用于指定Winsock版本号(如早期的windsock1.1,现在的winsock2.x)
    WSADATA        wsaData;                // 用于返回winsock版本信息
    SOCKET        sockfd, connfd;            // 套接字描述符、连接描述符
    SOCKADDR_IN servAddr, cliAddr;        // SOCKADDR_IN是一种通用Socket地址结构

    // 使用MAKEWORD宏可以给一个WORD类型赋值。此处表示主版本号为2,副版本号为2,即winsock2.2
    wVersionRequested = MAKEWORD(2,2);    
    
/////// 1. 加载Winsock的动态链接库,WSAStartup()
    
    // WSAStartup()会根据指定Winsock版本搜索相应的动态链接库并进行绑定,成功则返回0
    if (WSAStartup(wVersionRequested,&wsaData) != 0)
    {
        printf("Winsock init fail!\n");
        return (-1);        
    }

///////// 2. 创建一个可用的套接字,socket()

    // 第一个参数为int af,用来指定使用的地址族,TCP/IP协议为AF_INET(比较常用)、Xerox协议为AF_NS、UNIX协议为AF_UNIX等
    // 第二个参数为int type,用来指定创建的套接字类型,有SOCK_DGRAM、SOCK_STREAM、SOCK_RAW等
    // 第三个参数为int protocol,用来指定使用的具体协议,根据地址格式和Socket类型自动选择,通常设为0
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    // 返回值为套接字描述符SOCKET,若失败则返回INVALID_SOCKET
    if (sockfd == INVALID_SOCKET) {
        printf("Create socket fail!\n");
        WSACleanup();    // 释放资源
        return (-1);
    }

///////// 3. 将套接字与本地地址相互绑定,bind()
    
    // 将地址转为所需格式 
    // sin_family指定使用的地址族
    servAddr.sin_family = AF_INET;
    // sin_addr指定使用的IP,设为INADDR_ANY表示分配给本机的所有IP地址
    // htonl()将主机的无符号长整形数转换成网络字节顺序    
    servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    // sin_port指定使用的端口号
    // htos()将一个无符号短整型数值转换为网络字节序
    servAddr.sin_port = htons(6000);
    // 第一个参数为欲绑定套接字描述符,第二个参数为欲绑定本地地址,第三个参数为本地地址结构的长度
    if (bind(sockfd, (SOCKADDR*)&servAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) 
    {
        printf("Bind fail!\n");
        return (-1);
    }  else
        printf("Server running:\n");
    
    // 4. 将套接字设置为监听模式等待连接请求,listen()
    
    // 第一个参数为套接字描述符,第二个参数为客户连接请求队列的最大长度
    if(listen(sockfd, 5) == SOCKET_ERROR)
    {
        printf("Listen fail!\n");
        return (-1);
    }
    
    // 5. 设置等待连接请求,处理请求accept(), recv(), send()

    int len = sizeof(SOCKADDR);
    char buff[BUFFSIZE];
    int    count;

    while (true)
    {    
        // 阻塞式等待请求,接受连接
        if((connfd = accept(sockfd, (SOCKADDR*)&cliAddr, &len)) == SOCKET_ERROR)
        {
            printf("Accept fail!\n");
            return (-1);
        }

        printf("A client connect!\n");

        while(true) {
            // 接收数据
            if((count = recv(connfd, buff, BUFFSIZE, 0)) == SOCKET_ERROR) 
            {
                printf("Recv fail!\n");
                break;
            }

            buff[count] = '\0';
            printf("Receive:[%d bytes] %s\n", count, buff);    
            
            // 返回接收到的数据
            if((send(connfd, buff, strlen(buff), 0)) == SOCKET_ERROR)
            {
                printf("Send fail!\n");
                break;
            }

            if(!strcmp(buff, "88")) 
                break;
        }

        // 关闭套接字
        if (closesocket(connfd) == SOCKET_ERROR)
        {
            printf("Close fail!\n");
            return (-1);
        }
    }

    if (closesocket(sockfd) == SOCKET_ERROR)
    {
        printf("Close fail!\n");
        return (-1);
    }
    // 卸载Winsock的动态链接库
    if(WSACleanup() == SOCKET_ERROR)
    {
        printf("Clean fail!\n");
        return (-1);
    }
    return 0;
}

 

posted @ 2012-12-11 13:17  xjx_user  阅读(1220)  评论(0编辑  收藏  举报