windows下socket编程实现客户端和服务端互发消息

WinSock

在windows系统下有一个api专门提供socket编程,这个api即为WinSock,其中特别需要注意的是先需要调用WSAStartup函数初始化Windows Sockets API,结束后需要调用WSACleanup函数释放掉Windows Sockets DLL

socket编程

关于socket在客户端、服务端之间的通信可如图所示:
在这里插入图片描述
其中socket()函数是创建套接字函数,其函数如下:socket(int af,int type,int protocol) 其中af代表地址族,其中AF_INET代表TCP协议,type代表套接字的类型,套接字一般分为三种类型,即流式套接字数据报套接字原始套接字,protocol代表协议。
bind()是绑定套接字函数,其函数原型如下:bind(SOCKET s,const sockaddr*addr,int namelen),其中s代表套接字,addr代表一个数据结构sockaddr_in,其结构如下:
struct sockaddr_in{
u_char sin_len;//地址长度
u_char sin_family;//地址族(tcp/ip:AF_INET)
u_short sin_port;//端口号
struct in_addr sin_addr;//ip地址
char sin_zero[8];//未用,置零
}

一般而言只需服务端绑定即可。对于服务端绑定来说,由于服务端可以有多个网卡,可能有多个ip地址,其形式如图所示:
在这里插入图片描述
对于此可以采用地址通配符ANADDR_ANY,这样无论通过哪个地址都可以接受请求。
listen()函数是监听函数,其函数原型如下:listen(SOCKET S,int backlog) 其中s代表套接字,backlog代表监听队列的长度,一般而言监听队列的最大长度为20
accpet()函数是接受函数,当监听到连接请求之后,accept函数会返回一个新的套接字与客户端建立连接,而原来的套接字则继续等待连接请求。其函数原型如下:accept(SOCKET s,sockaddr*addr,int addrlen)
connect()函数是连接函数,是客户端与服务端的连接函数,其函数原型如下:connect(SOCKET S,const sockaddr*name,int namelen),其中name是指定的服务端的地址信息。
send()函数是发送数据函数,其函数原型如下:send(SOCKET*s,const char * buf,int len,int flags),其中flags未用,默认为0。
recv函数是接收数据函数,其函数原型如下:recv(SOCKET s,char*buf,int len,int flags)
closesocket()函数是关闭套接字函数,其函数原型如下:closesocket(SOCKET S)
其通信流程如下:首先服务端先创建套接字,然后绑定套接字,然后调用监听函数,其监听函数会把客户端的连接请求形成一个队列,然后调用accept()函数接受请求,创建一个新的套接字,让这个套接字与客户端套接字建立连接。之后调用recv()函数和send()函数实现客户端和服务端的数据接收和发送。在完成后,会调用closesocket()函数关闭套接字,这时服务端会返回到accept()函数中去继续接受请求。

代码

代码如下:
服务端:

#include<stdio.h>
#include<iostream>
#include<string>
#include<windows.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

int main(int argc, char* argv[]) {
	//初始化版本号
	WORD socketVersion = MAKEWORD(2, 2);
	WSADATA wsdata;
	if (WSAStartup(socketVersion, &wsdata) != 0) {
		return 1;
	}
	//创建套接字
	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (serverSocket == INVALID_SOCKET) {
		cout << "无效的套接字" << endl;
		return 1;
	}
	//绑定套接字
	sockaddr_in addr_in;
	addr_in.sin_family = AF_INET;
	addr_in.sin_port = htons(8888);
	addr_in.sin_addr.S_un.S_addr = INADDR_ANY;//地址通配符
	if (bind(serverSocket, (sockaddr*)&addr_in, sizeof(addr_in)) == SOCKET_ERROR) {
		cout << "绑定失败" << endl;
		return 1;
	}
	//监听套接字
	if (listen(serverSocket, 10) == SOCKET_ERROR) {
		cout << "监听失败" << endl;
		return 1;
	}
	SOCKET connection_socket;//调用accept()函数时产生的新的套接字
	sockaddr_in connection_addr_in;//新的套接字对应的地址
	char data[1000];//存储要转发的数据
	int flag = 0;//标志是否被连接
	int len = sizeof(connection_addr_in);
	while (true) {
		if (!flag) {
			cout << "正在连接中..." << endl;
		}
		connection_socket = accept(serverSocket, (sockaddr*)&connection_addr_in, &len);
		if (connection_socket == INVALID_SOCKET) {
			flag = 0;
			cout << "接受失败" << endl;
			return 1;
		}
		if (!flag) {
			cout << "收到一个连接:" << inet_ntoa(connection_addr_in.sin_addr) << endl;
			flag = 1;
		}
		int num = recv(connection_socket, data,100,0);
		if (num > 0) {
			data[num] = '\0';
			cout << "服务端收到:" << data << endl;
		}
		string data;
		getline(cin, data);
		const char* senddata;
		senddata = data.c_str();
		send(connection_socket, senddata, strlen(senddata), 0);
		closesocket(connection_socket);
	}
	closesocket(serverSocket);
	WSACleanup();//关闭
	return 0;

}

客户端代码:

#include<iostream>
#include<winsock2.h>
#include<string>
#include<Ws2tcpip.h>
#include<windows.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

int main() {
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if (WSAStartup(sockVersion, &data) != 0) {
		return 1;
	}
	while (true) {
		SOCKET clientsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//地址族,流式套接字,tcp协议
		if (clientsocket == INVALID_SOCKET) {
			cout << "Socket error" << endl;
			return 1;
		}
		sockaddr_in sock_in;//结构体,内包含地址族,端口,ip地址
		sock_in.sin_family = AF_INET;
		sock_in.sin_port = htons(8888);//htons函数是实现将端口号的顺序弄成与本地的一样,由于有大小端之分
		inet_pton(AF_INET, "192.168.200.113", &sock_in.sin_addr.S_un.S_addr);//将ip地址转化成二进制模式传给sock_in中的ip地址,传输的时候是以二进制传输的
		if (connect(clientsocket, (sockaddr*)&sock_in, sizeof(sock_in)) == SOCKET_ERROR) {
			cout << "连接失败!" << endl;
			return 1;
		}
		string data;
		const char *senddata;
		getline(cin, data);
		senddata = data.c_str();
		send(clientsocket, senddata, strlen(senddata), 0);//将数据发送给套接字
		char revdata[1000];
		int num = 0;
		num = recv(clientsocket, revdata, strlen(revdata), 0);//接收发过来的信息
		if (num > 0) {
			revdata[num] = '\0';
			cout << "客户端收到:" <<revdata << endl;
		}
		closesocket(clientsocket);//将套接字关闭
	}
	WSACleanup();//关闭
}

其中关于htons()函数主要是将本地字节顺序转换成网络字节顺序(两字节)由于机器的字节顺序有大端顺序和小端顺序,相应的也有ntohs()函数将网络字节顺序转化成本地字节顺序。
其结果如下:
(先启动服务端再启动客户端)
在这里插入图片描述
以上
参考:哈工大《计算机网络》,博客链接:c++socket编程

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一段 Linux 下使用 C 语言实现 socket 编程的示例代码(客户端): ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8888 #define MAXDATASIZE 100 int main(int argc, char *argv[]) { int sockfd, num; char buf[MAXDATASIZE]; struct sockaddr_in server; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket() error."); exit(1); } server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr.s_addr = inet_addr("127.0.0.1"); bzero(&(server.sin_zero), 8); if (connect(sockfd, (struct sockaddr *)&server, sizeof(struct sockaddr)) == -1) { perror("connect() error."); exit(1); } if ((num = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) { perror("recv() error."); exit(1); } buf[num - 1] = '\0'; printf("Server Message: %s\n", buf); close(sockfd); return 0; } ``` 这是一段 Linux 下使用 C 语言实现 socket 编程的示例代码(服务器端): ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #define PORT 8888 #define BACKLOG 1 int main() { int sockfd, new_fd; struct sockaddr_in server; struct sockaddr_in client; int sin_size; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket() error."); exit(1); } int opt = SO_REUSEADDR; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); server.sin_family = AF_INET; server.sin_port = htons(PORT); server.sin_addr ### 回答2: 下面是一个使用C语言编写的在Linux下使用Socket编程实现客户端服务端对话的示例代码: 服务端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define MAX_BUFFER_SIZE 1024 int main() { int server_socket, client_socket; struct sockaddr_in server_address, client_address; char buffer[MAX_BUFFER_SIZE]; int str_len; socklen_t client_address_size; server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket == -1) { perror("socket() error"); exit(1); } memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_port = htons(1234); server_address.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) { perror("bind() error"); exit(1); } if (listen(server_socket, 5) == -1) { perror("listen() error"); exit(1); } client_address_size = sizeof(client_address); client_socket = accept(server_socket, (struct sockaddr*)&client_address, &client_address_size); if (client_socket == -1) { perror("accept() error"); exit(1); } while (1) { str_len = read(client_socket, buffer, MAX_BUFFER_SIZE - 1); if (str_len == -1) { perror("read() error"); exit(1); } buffer[str_len] = '\0'; printf("Client: %s\n", buffer); printf("Server: "); fgets(buffer, MAX_BUFFER_SIZE, stdin); if (!strcmp(buffer, "q\n") || !strcmp(buffer, "Q\n")) { break; } write(client_socket, buffer, strlen(buffer)); } close(client_socket); close(server_socket); return 0; } ``` 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define MAX_BUFFER_SIZE 1024 int main() { int client_socket; struct sockaddr_in server_address; char buffer[MAX_BUFFER_SIZE]; int str_len; client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket == -1) { perror("socket() error"); exit(1); } memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_port = htons(1234); server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(client_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) { perror("connect() error"); exit(1); } while (1) { printf("Client: "); fgets(buffer, MAX_BUFFER_SIZE, stdin); if (!strcmp(buffer, "q\n") || !strcmp(buffer, "Q\n")) { break; } write(client_socket, buffer, strlen(buffer)); str_len = read(client_socket, buffer, MAX_BUFFER_SIZE - 1); if (str_len == -1) { perror("read() error"); exit(1); } buffer[str_len] = '\0'; printf("Server: %s\n", buffer); } close(client_socket); return 0; } ``` 以上是一个简单的客户端服务端对话的例子,运行服务端程序后,再运行客户端程序就可以实现在终端上进行对话。客户端输入的内容会发送给服务端,在服务端的终端显示,并等待服务端的回复,服务端在接收到客户端的消息后会回复给客户端,并在客户端的终端显示。当输入"q"或"Q"时,客户端服务端会断开连接并退出程序。 ### 回答3: 下面是一段使用C语言在Linux下实现客户端服务端对话的代码: 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #define BUF_SIZE 1024 int main() { int client_socket; struct sockaddr_in server_address; char buffer[BUF_SIZE]; // 创建套接字 client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket == -1) { perror("创建套接字失败"); exit(1); } // 设置服务器地址 memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = inet_addr("127.0.0.1"); // 服务器IP地址 server_address.sin_port = htons(8888); // 服务器端口号 // 连接服务器 if (connect(client_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) { perror("连接服务器失败"); exit(1); } while (1) { // 输入要发送的消息 printf("请输入要发送的消息(输入exit退出):"); fgets(buffer, BUF_SIZE, stdin); // 发送消息给服务器 if (send(client_socket, buffer, strlen(buffer), 0) == -1) { perror("发送消息给服务器失败"); exit(1); } // 退出循环的条件 if (strcmp(buffer, "exit\n") == 0) { break; } // 接收服务器返回的消息 memset(buffer, 0, BUF_SIZE); if (recv(client_socket, buffer, BUF_SIZE - 1, 0) == -1) { perror("接收服务器消息失败"); exit(1); } // 打印服务器返回的消息 printf("服务器消息:%s", buffer); } // 关闭套接字 close(client_socket); return 0; } ``` 服务端代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> #define BUF_SIZE 1024 int main() { int server_socket, client_socket; struct sockaddr_in server_address, client_address; socklen_t client_address_size; char buffer[BUF_SIZE]; // 创建套接字 server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket == -1) { perror("创建套接字失败"); exit(1); } // 设置服务器地址 memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_ANY); // 任意IP地址 server_address.sin_port = htons(8888); // 服务器监听端口号 // 绑定套接字 if (bind(server_socket, (struct sockaddr*)&server_address, sizeof(server_address)) == -1) { perror("绑定套接字失败"); exit(1); } // 监听套接字 if (listen(server_socket, 5) == -1) { perror("监听套接字失败"); exit(1); } printf("等待客户端连接...\n"); // 接受客户端连接 client_address_size = sizeof(client_address); client_socket = accept(server_socket, (struct sockaddr*)&client_address, &client_address_size); if (client_socket == -1) { perror("接受客户端连接失败"); exit(1); } printf("客户端连接成功\n"); while (1) { // 接收客户端发送的消息 memset(buffer, 0, BUF_SIZE); if (recv(client_socket, buffer, BUF_SIZE - 1, 0) == -1) { perror("接收客户端消息失败"); exit(1); } // 退出循环的条件 if (strcmp(buffer, "exit\n") == 0) { break; } // 打印客户端发送的消息 printf("客户端消息:%s", buffer); // 回复客户端消息 if (send(client_socket, buffer, strlen(buffer), 0) == -1) { perror("回复客户端消息失败"); exit(1); } } // 关闭套接字 close(client_socket); close(server_socket); return 0; } ``` 这段代码实现了一个简单的客户端服务端对话的功能。客户端使用`socket`函数创建了一个套接字,然后通过`connect`函数连接到服务器上。连接成功后,进入一个循环,循环中首先通过`fgets`函数输入要发送的消息,然后通过`send`函数将消息发送给服务器。接着接收服务器返回的消息,再打印出来。如果输入的消息是"exit",则退出循环,关闭套接字,程序结束。 服务端使用`socket`函数创建一个套接字,并设置服务器地址。然后通过`bind`函数将套接字与服务器地址绑定,并通过`listen`函数监听套接字。接着使用`accept`函数接受客户端连接,接收成功后进入一个循环。循环中首先接收客户端发送的消息,然后通过`send`函数将消息回复给客户端。如果接收到的消息是"exit",则退出循环,关闭套接字,程序结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值