socket 网络通信 ( windows + Linux )

参考:
C++ socket 网络通信等

Socket 编程

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭).
说白了Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

在这里插入图片描述
交互流程:
TCP/IP通信中,主要是进行C/S交互。废话不多说,下面看看具体交互内容:

服务端:建立socket,申明自身的port和IP,并绑定到socket,使用listen监听,然后不断用accept去查看是否有连接。如果有,捕获socket,并通过recv获取消息的内容,通信完成后调用closeSocket关闭这个对应accept到的socket。如果不需要等待任何客户端连接,那么用closeSocket直接关闭自身的socket。

客户端:建立socket,通过端口号和地址确定目标服务器,使用Connect连接到服务器,send发送消息,等待处理,通信完成后调用closeSocket关闭socket。

windows平台(C++):

以下编程平台在VS2015上,新建两个不同的控制台应用程序,一个只有服务器代码,另一个只有客户端代码,同时运行,即可实现服务器和客户端的通信
服务器代码:
//server.cpp
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;

void initialization();

int main() 
{
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;
	int len = 0;

	//定义发送缓冲区和接受缓冲区
	char send_buf[100];
	char recv_buf[100];

	//定义服务端套接字,接受请求套接字
	SOCKET s_server;
	SOCKET s_accept;

	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	SOCKADDR_IN accept_addr;
	initialization();

	//填充服务端信息
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(7777);

	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (bind(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) 
	{
		cout << "套接字绑定失败!" << endl;
		WSACleanup();
	}
	else 
	{
		cout << "套接字绑定成功!" << endl;
	}

	//设置套接字为监听状态
	if (listen(s_server, SOMAXCONN) < 0) 
	{
		cout << "设置监听状态失败!" << endl;
		WSACleanup();
	}
	else 
	{
		cout << "设置监听状态成功!" << endl;
	}
	cout << "服务端正在监听连接,请稍候...." << endl;

	//接受连接请求
	len = sizeof(SOCKADDR);
	s_accept = accept(s_server, (SOCKADDR *)&accept_addr, &len);
	if (s_accept == SOCKET_ERROR) 
	{
		cout << "连接失败!" << endl;
		WSACleanup();
		return 0;
	}
	cout << "连接建立,准备接受数据" << endl;

	//接收数据
	while (1) 
	{
		recv_len = recv(s_accept, recv_buf, 100, 0);
		if (recv_len < 0) 
		{
			cout << "接受失败!" << endl;
			break;
		}
		else 
		{
			cout << "客户端信息:" << recv_buf << endl;
		}
		cout << "请输入回复信息:";
		cin >> send_buf;
		send_len = send(s_accept, send_buf, 100, 0);
		if (send_len < 0) 
		{
			cout << "发送失败!" << endl;
			break;
		}
	}

	//关闭套接字
	closesocket(s_server);
	closesocket(s_accept);

	//释放DLL资源
	WSACleanup();
	return 0;
}

void initialization()
{
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) 
	{
		cout << "初始化套接字库失败!" << endl;
	}
	else 
	{
		cout << "初始化套接字库成功!" << endl;
	}

	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) 
	{
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else 
	{
		cout << "套接字库版本正确!" << endl;
	}

}
客户端代码:
//client.cpp
#include<iostream>
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;

void initialization();

int main() 
{
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;

	//定义发送缓冲区和接受缓冲区
	char send_buf[100];
	char recv_buf[100];

	//定义服务端套接字,接受请求套接字
	SOCKET s_server;

	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	initialization();

	//填充服务端信息
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	server_addr.sin_port = htons(7777);

	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) 
	{
		cout << "服务器连接失败!" << endl;
		WSACleanup();
	}
	else 
	{
		cout << "服务器连接成功!" << endl;
	}

	//发送,接收数据
	while (1)
	{
		cout << "请输入发送信息:";
		cin >> send_buf;
		send_len = send(s_server, send_buf, 100, 0);
		if (send_len < 0)
		{
			cout << "发送失败!" << endl;
			break;
		}
		recv_len = recv(s_server, recv_buf, 100, 0);
		if (recv_len < 0) 
		{
			cout << "接受失败!" << endl;
			break;
		}
		else 
		{
			cout << "服务端信息:" << recv_buf << endl;
		}

	}

	//关闭套接字
	closesocket(s_server);

	//释放DLL资源
	WSACleanup();
	return 0;
}
void initialization() 
{
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) 
	{
		cout << "初始化套接字库失败!" << endl;
	}
	else 
	{
		cout << "初始化套接字库成功!" << endl;
	}

	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2)
	{
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else 
	{
		cout << "套接字库版本正确!" << endl;
	}

}
现象:

在这里插入图片描述

Linux平台(C代码):

服务器代码:
/* File Name: server.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <unistd.h>

#define DEFAULT_PORT 8000
#define MAXLINE 4096

int main(int argc, char** argv)
{
    int    socket_fd, connect_fd;
    struct sockaddr_in  servaddr;
	char   sendline[4096];
    char   buff[4096];
    int    n;
	
    //初始化Socket
    if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
	{
		printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
		exit(0);
    }
	
    //初始化
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  //IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
    servaddr.sin_port = htons(DEFAULT_PORT);       //设置的端口为DEFAULT_PORT
 
    //将本地地址绑定到所创建的套接字上
    if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
	{
		printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
		exit(0);
    }
	
    //开始监听是否有客户端连接
    if( listen(socket_fd, 10) == -1)
	{
		printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
		exit(0);
    }
	
    printf("waiting for client's request...\n");

    //多个客户端与服务器建立连接,发送消息
	/*	
    while(1)
	{		
		//阻塞直到有客户端连接,不然多浪费CPU资源。
        if((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1)
		{
			printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
			continue;
        }
	
		//接受客户端传过来的数据
		n = recv(connect_fd, buff, MAXLINE, 0);		
		
		buff[n] = '\0';
		printf("recv msg from client: %s\n", buff);
				
    }
	*/
	
	 //单个客户端与服务器建立连接,发送消息并回复消息,多次通信
		
	if((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1) //阻塞直到有客户端连接,不然多浪费CPU资源。
	{
		printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
	}
	else
	{		
		printf("client connected!\n");		
	}
	
    while(1)
	{		
		//接受客户端传过来的数据
		n = recv(connect_fd, buff, MAXLINE, 0);				
		buff[n] = '\0';
		printf("recv msg from client: %s", buff);
		
		//给客户端发送消息
		printf("send msg to client:");
		fgets(sendline, 4096, stdin);
		if( send(connect_fd, sendline, strlen(sendline), 0) < 0)
		{
			printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
			exit(0);
		}
				
    }
		
	
	close(connect_fd);
    close(socket_fd);
}
客户端代码:
/* File Name: client.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <unistd.h>

#define MAXLINE 4096
 
int main(int argc, char** argv)
{
    int   sockfd, n, rec_len;
    char  recvline[4096], sendline[4096];
    char  buf[MAXLINE];
    struct sockaddr_in  servaddr;
 
    if( argc != 2)
	{
		printf("usage: ./client <ipaddress>\n");
		exit(0);
    }
 
    //初始化socket
    if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
		exit(0);
    }
 
 
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(8000);
	
	//将点分文本的IP地址转换为二进制网络字节序的IP地址
    if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
	{
		printf("inet_pton error for %s\n",argv[1]);
		exit(0);
    }
 
    //连接服务端
    if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
	{
		printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
		exit(0);
    }
	else
	{
		printf("connect server success!\n");
	}
 
	while(1)
	{
		printf("send msg to server:");
		fgets(sendline, 4096, stdin);
		if( send(sockfd, sendline, strlen(sendline), 0) < 0)
		{
			printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
			exit(0);
		}
		
		if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) 
		{
			perror("recv error");
			exit(1);
		}
		else
		{
			buf[rec_len]  = '\0';
			printf("recv msg from server:%s",buf);
		}
		
	}
    close(sockfd);
    exit(0);
}
Makefile:
ifeq ($(DEBUG),Enable)
	CPPFLAGS = -D_DEBUG -g -Wall
else
	CPPFLAGS = -s 
endif 

INCS=-I./include 
LIBS=./lib   
OUTDIR=./
TARGET_CLIENT=client
TARGET_SERVER=server

exe:	
	gcc $(CPPFLAGS) $(INCS)  client.c  -o $(OUTDIR)/$(TARGET_CLIENT)
	gcc $(CPPFLAGS) $(INCS)  server.c  -o $(OUTDIR)/$(TARGET_SERVER)
	
all: exe

clean:
	rm $(OUTDIR)/$(TARGET) 

现象:

首先启动服务器,等待客户端连接
在这里插入图片描述
然后客户端连接上来,准备发送信息
在这里插入图片描述
这时服务端已经收到了客户端连接
在这里插入图片描述
然后客户端发信息给服务端
在这里插入图片描述
服务端接收到信息
在这里插入图片描述
最终实现互相通信,当然这只是与一个客户端通信,放开服务端代码中的注释部分,可以实现多个客户端与服务器建立连接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值