用c++完成一个hello/hi的简单的网络聊天程序

1.什么是Socket

套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

Socket起源于Unix,而Unix/Linux 基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式 来操作。Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)。程序必须绑定一个端口才可以从网络上收发数据,这样,远程发到这个端口上的数据,就全会转给这个程序。

2.分类

为了满足不同的通信程序对通信质量和性能的要求,一般的网络系统提供了三种不同类型的套接字。

(1)流式套接字。它提供了一种可靠的、面向连接的双向数据传输服务,实现了数据无差错、无重复的发送。流式套接字内设流量控制,被传输的数据看作是无记录边界的字节流。

(2)数据报套接字。它提供了一种无连接、不可靠的双向数据传输服务。数据包以独立的形式被发送,并且保留了记录边界,不提供可靠性保证。

(3)原始套接字。该套接字允许对较低层协议(如IP或ICMP)进行直接访问,常用于网络协议分析,检验新的网络协议实现。

3.用到的API

server用到的API有socket,bind,listen,accept,read,write,close   (read和write可以用send和recv替换)

client用到的API有socket,connect,read,write,close    (read和write可以用send和recv替换)

通信流程如下:

1)服务端创建socket

2)绑定socket和端口号

3)监听该端口号

4)启动accept()用来接收来自客户端的连接请求,此时如果有连接则继续执行,否则将阻塞在这里。

5)客户端创建socket

6)客户端通过IP地址和端口号连接服务端,即tcp中的三次握手

7)连接成功,客户端可以向服务端发送数据

8)服务端读取客户端发来的数据

9)任何一端均可主动断开连接

4.Socket编程

服务器端:

#include <stdio.h>
#include <iostream>
#include <winsock2.h>
#include <WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")  

int main(int argc, char* argv[])
{
    //初始化WSA
    WORD sockVersion = MAKEWORD(2,2);
    WSADATA wsaData;
    if(WSAStartup(sockVersion, &wsaData)!=0)
    {
        return 0;
    }  

    //创建套接字
    SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(slisten == INVALID_SOCKET)
    {
        printf("socket error !");
        return 0;
    }  

    //绑定IP和端口
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(8888);
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
    {
        printf("bind error !");
    }  

    //开始监听
    if(listen(slisten, 5) == SOCKET_ERROR)
    {
        printf("listen error !");
        return 0;
    }  

    //循环接收数据
    SOCKET sClient;
    sockaddr_in remoteAddr;
    int nAddrlen = sizeof(remoteAddr);
    char revData[255];
    while (true)
    {
        printf("等待连接...\n");
        sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
        if(sClient == INVALID_SOCKET)
        {
            printf("accept error !");
            continue;
        }
        printf("接受到一个连接:%s \r\n", inet_ntop(AF_INET, (void*)&remoteAddr.sin_addr, revData, 16));
       // printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));  //inet_ntoa老函数在新版本VS+64位计算机上使用会报错

        //接收数据
        int ret = recv(sClient, revData, 255, 0);
        if(ret > 0)
        {
            revData[ret] = 0x00;
            printf(revData);
        }  

        //发送数据
        const char * sendData = "你好,TCP客户端!\n";
        send(sClient, sendData, strlen(sendData), 0);
        closesocket(sClient);
    }  

    closesocket(slisten);
    WSACleanup();
    return 0;
} 

客户端:

#include<WINSOCK2.H>
#include<STDIO.H>
#include<iostream>
#include<cstring>
#include <WS2tcpip.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable:4996)
int main()
{
    WORD sockVersion = MAKEWORD(2, 2);
    WSADATA data;
    if(WSAStartup(sockVersion, &data)!=0)
    {
        return 0;
    }
    while(true){
        SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(sclient == INVALID_SOCKET)
        {
            printf("invalid socket!");
            return 0;
        }

        sockaddr_in serAddr;
        serAddr.sin_family = AF_INET;
        serAddr.sin_port = htons(8888);
        //serAddr.sin_addr.S_un.S_addr = inet_pton(AF_INET, "127.0.0.1", &serAddr);
        serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
        if(connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
        {  //连接失败
            printf("connect error !");
            closesocket(sclient);
            return 0;
        }

        string data;
        cin>>data;
        const char * sendData;
        sendData = data.c_str();   //string转const char*
        //char * sendData = "你好,TCP服务端,我是客户端\n";
        send(sclient, sendData, strlen(sendData), 0);
        //send()用来将数据由指定的socket传给对方主机
        //int send(int s, const void * msg, int len, unsigned int flags)
        //s为已建立好连接的socket,msg指向数据内容,len则为数据长度,参数flags一般设0
        //成功则返回实际传送出去的字符数,失败返回-1,错误原因存于error 

        char recData[255];
        int ret = recv(sclient, recData, 255, 0);
        if(ret>0){
            recData[ret] = 0x00;
            printf(recData);
        }
        closesocket(sclient);
    }

    WSACleanup();
    return 0;

}

1)socket()函数

Linux中函数形式为:

int socket(int domain, int type, int protocol);

domain即协议域,又称为协议族(family)。对TCP/IP协议族而言,该参数应该设置为PF_INET或PF_INET6,分别对应IPv4和IPv6.

type用来指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM

protocol指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP

C++中:

SOCKET PASCAL FAR socket(int af, int type, int protocol);

2)bind()函数

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

bind将my_addr所指的socket地址分配给未命名的socketfd文件描述符,addrlen参数指出该socket地址的长度。bind成功时返回0,失败则返回-1并设置errno,常见为EACCES和EASSRINUSE,前者代表被绑定的地址是受保护的地址,仅超级用户能够访问,后者表示被绑定的地址正在使用中。

struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};

在c++下,函数形式为

int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR * name, int namelen);

3)listen()、connect()函数

 int listen(int sockfd, int backlog);

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

对于服务器来说,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。

listen()函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket队列中允许的连接数目。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

connect()函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度,客户端通过三次握手来建立与TCP服务器的连接。

在c++下,函数形式为

int PASCAL FAR listen(SOCKET s, int backlog);
int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR * name, int namelen);

listen()中,参数s标识一个本地已建立、尚未连接的套接字号,服务器愿意从它上面接收请求。backlog表示请求连接队列的最大长度,用于限制排队请求的个数。如果没有错误发生,listen()返回0。否则它返回SOCKET_ERROR。

connect()中,参数s是欲建立连接的本地套接字描述符,参数name指出说明对方套接字地址结构的指针,对方套接字地址长度由namelen说明。

4)accept()函数

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

TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数接收请求,这样连接就建立好了,之后就可以开始I/O操作了。

accept()函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,第三个参数为协议地址的长度。如果accpet成功,那么其返回值是一个全新的描述字,返回客户的TCP连接。

在c++中,函数形式为

SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);

参数s为本地套接字描述符,在用accept()调用参数前应该先调用过listen()。addr 指向客户方套接字地址结构的指针,用来接收连接实体的地址。addr的确切格式由套接字创建时建立的地址族决定。addrlen 为客户方套接字地址的长度(字节数)。如果没有错误发生,accept()返回一个SOCKET类型的值,表示接收到的套接字的描述符。否则返回INVALID_SOCKET。

5)send()、recv()函数

int send(int sockfd, const void *msg, int len, int flags);
int recv(int sockfd, void *buf, int len, unsigned int flags);

在c++下,函数形式为

int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags);
int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags);

6)close()函数

int close(int fd);

当完成了读写操作就要关闭相应的socket描述字,此时用close()函数。

在c++下,函数形式为

int PASCAL FAR closesocket ( IN SOCKET s);

原文地址:https://www.cnblogs.com/ft-97/p/12024908.html

时间: 2024-08-29 22:01:35

用c++完成一个hello/hi的简单的网络聊天程序的相关文章

以您熟悉的编程语言为例完成一个hello/hi的简单的网络聊天程序

在这片博文我们将使用python完成一个hello/hi的简单的网络聊天程序 先做一下准备工作 1.linux的socket基础api: 使用socket()创建套接字 int socket(int af, int type, int protocol); af为IP地址类型,AF_INE和AF_INET6分别对应ipv4和ipv6地址type是数据传输方式,Sock_stream(面向连接套接字)和sock_dgram(无连接套接字)protocol是传输协议,IPPROTO_TCP和IPPR

基于Python完成一个hello/hi的简单的网络聊天程序

一.Socket 套接字简介 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信.网络套接字是IP地址与端口的组合. 传输层实现端到端的通信,因此,每一个传输层连接有两个端点.那么,传输层连接的端点是什么呢?不是主机,不是主机的IP地址,不是应用进程,也不是传输层的协议端口.传输层连接的端点叫做套接字(socket).根据RFC793的定义:端口号拼接到IP

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

我选择使用python来实现hello/hi的简单网络聊天程序,源代码包括两个部分,客户端代码和服务器端代码,源代码部分如下图所示: 服务器端代码 1 import socket 2 3 HOST = '127.0.0.1' 4 PORT = 8888 5 6 server = socket.socket() 7 server.bind((HOST, PORT)) 8 server.listen(1) 9 10 print(f'the server is listening at {HOST}:

一个hello/hi的简单的网络聊天程序和python Socket API与Linux Socket API之间的关系

1.Socket概述 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信.网络套接字是IP地址与端口的组合. 套接字可以看成是两个网络应用程序进行通信时,各自通信连接中的一个端点.通信时,其中的一个网络应用程序将要传输的一段信息写入它所在主机的Socket中,该Socket通过网络接口卡的传输介质将这段信息发送给另一台主机的Socket中,使这段信息能传送到

使用python实现一个hello/hi的简单的网络聊天程序

一.TCP/IP协议通信原理 TCP/IP协议包含的范围非常的广,它是一种四层协议,包含了各种硬件.软件需求的定义.TCP/IP协议确切的说法应该是TCP/UDP/IP协议.UDP协议(User Datagram Protocol 用户数据报协议),是一种保护消息边界的,不保障可靠数据的传输.TCP协议(Transmission Control Protocol 传输控制协议),是一种流传输的协议.他提供可靠的.有序的.双向的.面向连接的传输. 保护消息边界,就是指传输协议把数据当作一条独立的消

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

使用Java的Socket实现客户端和服务器端之间的连接,实现客户端重复发送数据到服务器端的功能.即,用户可以在控制台不断输入内容,并将内容逐一发送给服务端.并在服务端显示. socket定义 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.建立网络通信连接至少要一对端口号(socket).socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装

使用python完成一个hello/hi的简单的网络聊天程序

在这篇文章中,我将先简要介绍socket原理,然后给出一个利用Python实现的简单通信样例,最后通过跟踪系统调用来分析Python中socket函数与Linux系统调用的对应关系. 1.socket简介 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议.下图为原理图: 2.简单通信样例

使用java实现一个hello/hi的简单的网络聊天程序

1.socket原理 Socket实质上提供了进程通信的端点.进程通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的.正如打电话之前,双方必须各自拥有一台电话机一样. 套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认. 1.服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态. 2.客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字.为此,客户端的套接字必须首先描述它要连接的服务器

java实现hello/hi的简单的网络聊天程序与ServerSocket调用栈跟踪

java实现hello/hi的简单的网络聊天程序 网络聊天采用TCP协议通过java实现 import java.io.*; import java.net.Socket; public class Client { public static void main(String[] args) throws Exception{ Socket socket = new Socket("192.168.31.68", 6666); BufferedReader reader = new