一个简单的hello/hi的网络聊天程序

什么是Socket编程?

我们平常使用的电脑或手机等电子产品,在大部分情况下同一电子产品都会同时运行多个应用程序,比如说微信、QQ等,那为什么一台电子产品可以同时运行多个应用程序呢?比如说你在手机上同时运行着微信和QQ,而我现在给你的微信发了一条信息,当这条信息到达你的手机上时,它怎么知道应该将这条信息交给微信而不是QQ的?这是因为每一个应用程序都打开了一个Socket并绑定到了一个端口上,当信息到达手机后,再根据端口发送给相应地应用程序。
常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对面向连接的TCP服务应用;数据报式是一种无连接的Socket,对应于无连接的UDP服务应用。接下来,分服务器端和客户端两部分来详细介绍流式Socket的实现。

服务器端:

1.调用socket()函数创建一个Socket

//socket()函数原型
SOCKET socket(  
      int af, //地址族,一般是AF_INET,表示使用IP地址族  
      int type, //socket类型,SOCK_STREAM或SOCK_DGRAM  
      int protocol//协议类型,通常取值 0
      )

2.调用bind()函数将创建的套接字与主机上提供服务的某端口绑定在一起

//bind()函数原型
int bind(
    SOCKET s, //要绑定的套接字
    const struct sockaddr FAR * name, //指向SOCKADDR结构的地址
    int namelen//地址参数(name)的长度
    );

3.调用listen()函数指示绑定后的套接字sockServer监听客户端的请求

//listen()函数原型
int listen(
      SOCKET s, //进行监听的socket
      int backlog//客户端可以连接的请求个数
      );

4.监听到有请求后调用accept()函数接受连接请求

SOCKET accept(
             SOCKET s, //处于监听状态的socket
             struct sockaddr FAR * addr, //客户机IP地址的sockaddr指针
             int FAR * addrlen//地址的长度
             );

5.在已经建立的连接上调用send()/recv()函数发送或接收数据

//send()函数原型
int send(
    SOCKET s,
    const char FAR * buf, //发送数据缓冲区 
    int len, //缓冲区长度
    int flags //用于控制数据传输方式,0表示按正常方式发送数据
    );
//recv()函数原型
int recv(
    SOCKET s,
    char FAR * buf, //接收数据缓冲区
    int len, //缓冲区长度
    int flags // 0表示接收的是正常数据,无特殊行为
    );

服务器端完整代码为:

//server.cpp
#include< iostream>
#include< winsock2.h>
#include< WS2tcpip.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
constexpr auto serverIP = "127.0.0.1";//服务器端的IP地址,这里设为本机回环地址;
constexpr auto serverPort = 6789;//服务器端进程的端口号,值大于1023即可;
int main() {
    //第一步:WinSock初始化
    WORD wVersion = MAKEWORD(2, 2);
    WSADATA wsaData;
    //WSAStartup,Windows Sockets Asynchronous,Windows异步套接字的启动命令。
    if (WSAStartup(wVersion, &wsaData)) {
        cout << "WSAStartup......\n";
        return 0;
    }
    //创建socket
    SOCKET sockServer;
    sockServer = socket(AF_INET, SOCK_STREAM, 0);
    //服务器地址
    SOCKADDR_IN serverAddr, clientAddr;
    serverAddr.sin_family = AF_INET;
    in_addr dst;
      //点分十进制的IPv4地址转换为网络字节的IP(整型)
    int res = inet_pton(AF_INET, serverIP, (void*)&dst);
      //将serverIP地址转换为in_addr的结构体,并复制在dst中
    if (res == 1) {
        serverAddr.sin_addr.S_un.S_addr = dst.s_addr;
    }
    else {
        cout << "The address is error." << endl;
    }
    serverAddr.sin_port = htons(serverPort);//host to network short 
    //绑定
    bind(sockServer, (SOCKADDR*)&serverAddr, sizeof(SOCKADDR));
    //监听
    listen(sockServer, 10);//10(服务器最多可以处理的用户数)
    //处理
    int acceptResult = sizeof(SOCKADDR);
    SOCKET sockAccept;
    cout << "The server is waiting for client's connection request..." << endl;
    sockAccept = accept(sockServer, (SOCKADDR*)&clientAddr, &acceptResult);
    if (sockAccept == INVALID_SOCKET) {
        cout << "The server failed to accept the client's connection request." << endl;
        return 0;
    }
    else {
        cout << "The server accepted the client' connection request." << endl;
    }
    //cout <<clientAddr.sin_addr .S_un.S_addr<< endl;
    //cout << clientAddr.sin_port;
    //传送、接收数据
    char sendbuffer[256] = { '\0' };
    char recivebuffer[256] = { '\0' };
    for (;;) {
        //接收数据
        recv(sockAccept, recivebuffer, 256, 0);
        if (strcmp(recivebuffer, "quit") == 0) {
            cout << "client quit." << endl;
            break;
        }
        else {
            cout << "Client says:>"<<recivebuffer<<endl;
            cout << "Server says:>" ;
            cin >> sendbuffer;
            send(sockAccept, sendbuffer, strlen(sendbuffer) + 1, 0);            
        }
    }
    closesocket(sockServer);
    WSACleanup();
    return 0;
}

客户端:

1.调用socket()函数创建一个Socket,代码为:

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

2.调用bind()函数将创建的套接字与主机上提供服务的某端口绑定在一起

//connect()函数原型
int connect(
       SOCKET s, //将要连接的socket 
           const struct sockaddr FAR * name, //目标socket地址
       int namelen //地址参数(name)的长度
           );

3.在已经建立的连接上调用send()/recv()函数发送或接收数据,对应的代码为:

//设发送缓存为sendbuffer;
send(sockClient, sendbuffer, strlen(sendbuffer) + 1, 0);
//设接收缓存为recivebuffer;
recv(sockClient, receivebuffer, 256, 0);

客户端完整代码为:

//client.cpp
#include< iostream>
#include< winsock2.h>
#include< ws2tcpip.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
constexpr auto serverIP = "127.0.0.1";//服务器端的IP地址;
constexpr auto serverPort = 6789;//服务器端进程的端口号;
int main() {
    //WinSock初始化
    WORD wVersion = MAKEWORD(2, 2);
    WSADATA wsaData;
    //WSAStartup,Windows Sockets Asynchronous,Windows异步套接字的启动命令。
    if (WSAStartup(wVersion, &wsaData)) {
        cout << "WSAStartup......" << endl;
        return 0;
    }
    //创建套接字
    SOCKET sockClient;
    sockClient = socket(AF_INET, SOCK_STREAM, 0);//1.address family,地址族协议,IPv4
                                                 //2.流式套接字
                                                 //3.已使用TCP,默认0
    //服务器地址
    SOCKADDR_IN  serverAddr;
    serverAddr.sin_family = AF_INET;
    in_addr dst;
       //点分十进制的IPv4地址转换为网络字节的IP(整型)
    int IPResult = inet_pton(AF_INET, serverIP, &dst);
      
    //将serverIP地址转换为in_addr的结构体,并复制在dst中
    if (IPResult == 1) {
        serverAddr.sin_addr.S_un.S_addr = dst.s_addr;
    }
    else {
        cout << "The IP is error." << endl;
    }
    serverAddr.sin_port = htons(serverPort);
    //请求连接
    int connectResult = connect(sockClient, (SOCKADDR*)&serverAddr, sizeof(SOCKADDR));
    if (connectResult != 0) {
        cout << "The client's connection request failed." << endl;
    }
    else {
        cout << "The client's connection request is accepted." << endl;
    }
    //发送及接收数据
    char sendbuffer[256] = { '\0' };
    char receivebuffer[256] = { '\0' };
    for (;;) {
        //发送数据
        cout << "The client says:>";
        cin >> sendbuffer;
        if (strcmp(sendbuffer, "quit") == 0) {
            break;
        }
        else {
            send(sockClient, sendbuffer, strlen(sendbuffer) + 1, 0);
        }
        //接收数据
        recv(sockClient, receivebuffer, 256, 0);
        cout << "The server says:>" << receivebuffer << endl;
    }
    closesocket(sockClient);//关闭套接字
    WSACleanup();//注销及释放分配资源
    return 0;
}

注:在客户端并没有为其指定端口号,而是由系统为其分配。

运行结果:

猜你喜欢

转载自www.cnblogs.com/minhui/p/12002050.html
今日推荐