我们经常使用微信、QQ等聊天工具,那我们能不能也写一个可以聊天的程序呢?

当然可以!

今天我们就用C++来写一个可以聊天的程序。

一、什么是socket?

socket顾名思义就是套接字的意思,用于描述地址和端口,是一个通信链的句柄。应用程序通过socket向网络发出请求或者回应。

socket编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW),前两者较常用。基于TCP的socket编程是流式套接字。

二、TCP/IP协议

要理解socket必须的得理解tcp/ip,它们之间好比送信的线路和驿站的作用,比如要建议送信驿站,必须得了解送信的各个细节。

TCP/IP协议不同于iso的7个分层,它是根据这7个分层,将其重新划分,好比打扫卫生,本来有扫帚,垃圾斗,抹布,涂料,盆栽等就好比OSI的标准几个分层,tcp/ip根据用途和功能,将扫帚,垃圾斗放到粗略整理层,抹布涂料放到中度整理层,盆栽放到最终效果层。这里TCP/IP也对OSI的网络模型层进行了划分:大致如下:

TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中

应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等

传输层:TCP,UDP

网络层:IP,ICMP,OSPF,EIGRP,IGMP

数据链路层:SLIP,CSLIP,PPP,MTU

每一抽象层建立在低一层提供的服务上,并且为高一层提供服务,看起来大概是这样子的

(以上来自(2条消息) Socket编程(简单(C++)实现TCP通信)_一包辣条包邮!的博客-CSDN博客_c++ socket编程)

接下来是实现:

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

首先加入以上的代码。接下来我会把会用到的函数写下来。

WSAStartup()

初始化

定义

WINSOCK_API_LINKAGE _Must_inspect_result_ int WSAAPI
WSAStartup(_In_ WORD wVersionRequested,_Out_ LPWSADATA lpWSAData);

第一个参数就是使用的WinSock的版本号,一般填0x0202或MAKEWORD(2, 2),

第二个参数直接填new WSADATA()即可。

如果返回值不是0,那么WSAStartup()就是出错了。

socket()

获得一个可用的socket

定义

WINSOCK_API_LINKAGE _Must_inspect_result_ SOCKET WSAAPI
socket(_In_ int af,_In_ int type,_In_ int protocol);

第一个参数指定发生的区域,可以填AF_UNIX、AF_INET、AF_NS(DOS、Windows仅支持AF_INET)。

第二个参数指定套接字的类型,这里有三种:SOCK_STREAM(流SOCKET,TCP)、SOCK_DGRAM(数据报文,UDP)、SOCK_RAW(原始套接字)。

第三个参数说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。

bind()

负责绑定端口。

定义

WINSOCK_API_LINKAGE int WSAAPI
bind(_In_ SOCKET s,_In_reads_bytes_(namelen)const struct sockaddr FAR * name,_In_ int namelen);

s就是我们刚刚得到的Socket句柄,填上去没多大难度。

如果返回值是SOCKET_ERROR,那么bind()就是出错了。

name是socket的设置,一般这样填:


Sockaddr_in addr;
addr.sin_family = 2;
addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY)
addr.sin_port = htons(PORT);bind(s,addr,sizeof addr);

PORT是需要填写的端口号。

namelen是addr结构体的大小。

listen()

监听

定义

WINSOCK_API_LINKAGE int WSAAPI
listen(_In_ SOCKET s,_In_ int backlog);

s就是我们刚刚得到的Socket句柄,填上去没多大难度(见bind)。

backlog是listen的最大数目,一般填5。

如果返回值是SOCKET_ERROR,那么listen()就是出错了。

accept()

等待连接

定义

WINSOCK_API_LINKAGE _Must_inspect_result_ SOCKET WSAAPI
accept(_In_ SOCKET s,
_Out_writes_bytes_opt_(*addrlen) struct sockaddr FAR * addr,
_Inout_opt_ int FAR * addrlen);//啊!!

(呜呜呜好多啊)

s就是我们刚刚得到的Socket句柄,填上去没多大难度(见bind)。

如果没有需要的话,余下的参数都填NULL。

connect()

连接服务器

定义

WINSOCK_API_LINKAGE
int
WSAAPI
connect(_In_ SOCKET s,_In_reads_bytes_(namelen) const struct sockaddr FAR * name,_In_ int namelen);

一般都这么用

SOCKADDR_IN addr;
addr.sin_addr.S_un.s_addr = inet_addr(/*服务器IP*/);
addr.sin_family = 2;
addr.sin_port = htons(58250);connect(s,(PSOCKADDR)&addr,sizeof addr);

当然,s就是还是我们刚刚得到的Socket句柄。如果返回值是SOCKET_ERROR,那么connect()就是出错了。

send()/recv()

终于到了发送和接收环节,让我们来看下定义。

WINSOCK_API_LINKAGE
int
WSAAPI
recv(_In_ SOCKET s,_Out_writes_bytes_to_(len, return)__out_data_source(NETWORK) char FAR * buf,_In_ int len,_In_ int flags);

s是对方的socket,buf是我们存储recv到的消息的char指针,len是char数组的大小,最后一个参数没有用的话就填0。

WINSOCK_API_LINKAGE
int
WSAAPI
send(_In_ SOCKET s,_In_reads_bytes_(len) const char FAR * buf,_In_ int len,_In_ int flags);

s是对方的socket,buf是我们存储要send的消息的char指针,len是char数组的大小,最后一个参数没有用的话就填0。如果返回值是SOCKET_ERROR,那么send()/recv()就是出错了。

closesocket()

关闭一个socket连接

int closesocket(SOCKET s);

s是要关闭的socket连接。

现在话不多说,直接上代码。

Server代码:

// Chat Server.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#pragma comment(lib,"ws2_32.lib")
#include <iostream>
#include <Winsock2.h>
#include<algorithm>
#include<Assert.h>
#include <vector>
#include<Windows.h>
#include <thread>
#include <string>
#include<mutex>
using namespace std;
#define err SOCKET_ERROR
vector<SOCKET> user = vector<SOCKET>();
thread tr;SOCKET sock;void Accept()
{while (true){char buf[1024] = {0};SOCKET s = accept(sock, NULL, NULL);if(s == SOCKET_ERROR)continue;user.push_back(s);}
}
int main()
{system("chcp 65001");WSADATA data;if (WSAStartup(MAKEWORD(2, 2), &data) != 0){cout << "Wsa start error!";WSACleanup();system("pause");return -1;}sock = socket(PF_INET, SOCK_STREAM, 0);sockaddr_in addr;addr.sin_family = 2;addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addr.sin_port = htons(54188);if (bind(sock, (sockaddr*)&addr, sizeof(addr)) == err){cout << "Bind start error!";WSACleanup();system("pause");return -1;}if (listen(sock, 20) == err){cout << "Listen start error!" << ::GetLastError();WSACleanup();system("pause");return -1;}cout << "running\n";tr = thread(Accept);while (true) {string str;cin >> str;for(auto var:user){send(var,str.c_str,str.size(),0);}}
}//好吧有点辣鸡,不过我写不出更好的了。

还有一种服务器。

// Chat Server2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//#pragma comment(lib,"ws2_32.lib")
#include <iostream>
#include <Winsock2.h>
#include<algorithm>
#include<Assert.h>
#include <vector>
#include<Windows.h>
#include <thread>
#include <string>
#include<mutex>
using namespace std;
struct Sock
{SOCKET sk;string name;Sock(SOCKET s, string n){name = n; sk = s;}
};
#define err SOCKET_ERROR
vector<Sock> user = vector<Sock>();
thread tr;SOCKET sock;
void sn(Sock s,int top)
{string tb = "Welcome to chat system," + s.name+"\n\n\n\n\t";send(s.sk, tb.c_str(), strlen(tb.c_str()), 0);while (true){char buf[1024] = { 0 };int iLen = recv(s.sk, buf, 1024, 0);if (iLen ==-1){string name= s.name+" exit.";std::cout << "user "<<s.name<<" is exit\nIn "<<top<<".";closesocket(s.sk);for (size_t i = 0; i < user.size(); i++){send(user[i].sk,name.c_str(), strlen(name.c_str()), 0);}return;}cout << buf<<std::endl;for (size_t i = 0; i < user.size(); i++){send(user[i].sk, buf, iLen, 0);}}
}void Accept()
{while (true){char buf[1024] = {0};SOCKET s = accept(sock, NULL, NULL);recv(s, buf, 1024, 0);user.push_back(Sock(s, buf));thread* th = new thread(sn, user[user.size() - 1], user.size());cout << "new user connect!\n";cout << "list:\n";for (auto var : user){cout << var.name << ",";}cout << endl;}
}
int main()
{system("chcp 65001");WSADATA data;if (WSAStartup(MAKEWORD(2, 2), &data) != 0){cout << "Wsa start error!";WSACleanup();system("pause");return -1;}sock = socket(PF_INET, SOCK_STREAM, 0);sockaddr_in addr;addr.sin_family = 2;addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addr.sin_port = htons(54188);if (bind(sock, (sockaddr*)&addr, sizeof(addr)) == err){cout << "Bind start error!";WSACleanup();system("pause");return -1;}if (listen(sock, 20) == err){cout << "Listen start error!" << ::GetLastError();WSACleanup();system("pause");return -1;}cout << "running\n";tr = thread(Accept);while (true) {::Sleep(10);}
}

客户端代码:

开玩笑了,在这里:

// Chat Server.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#pragma warning(disable : 4996)//新版的visual studio会警告我们不要用旧的
//函数,不过为了保险起见,还是屏蔽下警告吧。
#pragma comment(lib,"ws2_32.lib")
#include <iostream>
#include <Winsock2.h>
#include<algorithm>
#include<Assert.h>
#include <vector>
#include<Windows.h>
#include <thread>
#include <string>
#include<mutex>
using namespace std;
#define err -1
SOCKET sock;int main()
{system("chcp 65001");WSADATA data;if (WSAStartup(MAKEWORD(2, 2), &data) != 0){cout << "WsaStartup error!";WSACleanup();system("pause");return -1;}sock = socket(PF_INET, SOCK_STREAM, 0);sockaddr_in addr;addr.sin_family = 2;addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//IP//有些编译器是addr.sin_addr.S_addraddr.sin_port = htons(54188);if (connect(sock, (sockaddr*)&addr, sizeof(addr)) == err){cout << "connect error!";WSACleanup();system("pause");return -1;}cout << "running\n";while(true){    char s[4096] = {0};recv(sock,s,4096,0);cout<<s;}
}

用C++写一个客户端是一个不太保险的行为,如果可以的话建议用C#写一个。

C++ TCP Socket的使用(阻塞)相关推荐

  1. Socket 基础 非阻塞式TCP socket,实现文件传输jpg,mov等格式

    Socket 基础 非阻塞式TCP socket 实现文件传输, 实测 传输5M的jpg , 30M的 NEF(单反原图) 以及 1G以上的mov文件,均正常接收 客户端可多开,服务器多线程实现服务器 ...

  2. C# Socket编程(5)使用TCP Socket

    TCP 协议(Transmission Control Protocol,传输控制协议)是TCP/IP体系中面向连接(connection oriented)的传输层(transport layer) ...

  3. 转Go语言TCP Socket编程

    授权转载: Tony Bai 原文连接: https://tonybai.com/2015/11/17/tcp-programming-in-golang/ Golang的主要 设计目标之一就是面向大 ...

  4. golang TCP Socket编程

    Golang的主要 设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程序必不可少也是至关重要的一部分.在日常应用中,我们也可以看到Go中的net以及其subdirectories下的包均 ...

  5. java tcp read_【Java TCP/IP Socket】TCP Socket通信中由read返回值造成的的死锁问题(含代码)(转)...

    书上示例 在第一章<基本套接字>中,作者给出了一个TCP Socket通信的例子--反馈服务器,即服务器端直接把从客户端接收到的数据原原本本地反馈回去. 书上客户端代码如下: 书上的服务器 ...

  6. 【Java TCP/IP Socket】TCP Socket通信中由read返回值造成的的死锁问题(含代码)(转)...

    书上示例 在第一章<基本套接字>中,作者给出了一个TCP Socket通信的例子--反馈服务器,即服务器端直接把从客户端接收到的数据原原本本地反馈回去. 书上客户端代码如下: 1 2 3 ...

  7. 深入理解阻塞socket和非阻塞socket

    什么是阻塞socket,什么是非阻塞socket.对于这个问题,我们要先弄清什么是阻塞/非阻塞.阻塞与非阻塞是对一个文件描述符指定的文件或设备的两种工作方式. 阻塞的意思是指,当试图对该文件描述符进行 ...

  8. [Python]再学 socket 之非阻塞 Server

    再学 socket 之非阻塞 Server 本文是基于 python2.7 实现,运行于 Mac 系统下 本篇文章是上一篇初探 socket 的续集, 上一篇文章介绍了:如何建立起一个基本的 sock ...

  9. Go语言TCP Socket编程

    2019独角兽企业重金招聘Python工程师标准>>> Go语言TCP Socket编程 Golang的 主要 设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程序必不 ...

  10. TCP socket和web socket的区别

    小编先习惯性的看了下某中文百科网站对Web Socket的介绍,觉得很囧.如果大家按照这个答案去参加BAT等互联网公司的前端开发面试,估计会被鄙视. 还是让我们阅读一些英文材料吧. 让我们直接看sta ...

最新文章

  1. VScode的撤销操作的快捷键
  2. 段错误linux 内存不够,c - 为什么我的程序在linux-gcc而不是mingw-gcc上出现段错误? - 堆栈内存溢出...
  3. leetcode算法题--左旋转字符串
  4. 游戏中每日刷新实现思路浅析
  5. IIS设置Access-Control-Allow-Origin
  6. 每日一笑 | 爱的魔力转圈圈~
  7. 使用Epoll 在 Linux 上开发高性能应用服务器
  8. maven+SSM框架工程搭建
  9. vb net的定时循环_.NET工具ReSharper:如何帮助Visual Studio用户?
  10. 解释一下全连接层CNN中全连接层是什么样的
  11. 编译安装LAMP对其性能压力测试
  12. (附源码)Springboot酒店预订管理系统 毕业设计 100908
  13. flash用的什么语言
  14. 第二课 介绍:手绘墙画颜料选择
  15. 11 Animation动画
  16. 综述阅读:人工智能在肺癌影像基因组学方面的研究与进展
  17. Embodied Question Answering导读
  18. TPT Autotester|高效快捷的车辆在环ViL测试工具
  19. Lua--pairs和ipairs遍历区别
  20. [2019 年百度之星·程序设计大赛 - 初赛三]简要题解?

热门文章

  1. java运行环境配置_配置java开发运行环境的步骤
  2. 关于货币符号以及发音、币别码
  3. 分页的时候pageSize没有效果
  4. PS批量修改文件大小及类型
  5. 2007年计算机工程师,全国计算机等级考试四级数据库工程师考试大纲(2007年版)...
  6. Excel画竖着的折线图
  7. Android webview调用本地文件选择失败解决
  8. 【php】相对路径/绝对路径报错?
  9. [Matlab]使用textscan读取.csv文件时候只读取到了第一行
  10. DeepFaceLab:手动提取高精度脸图,减少抖动!