一个简单的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;
}

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

运行结果:

posted on 2019-12-07 16:02  MinHui  阅读(267)  评论(0)    收藏  举报

导航