C++基于TCP(面向连接)/UDP(面向无连接)的socket编程

基于TCP(面向连接)的socket编程步骤

服务器端程序:

1、创建套接字(socket);

2、将套接字绑定到一个本地地址和端口上(bind);

3、将套接字设为监听模式,准备接收客户请求(listen);

4、等待客户请求到来。当请求到来后,接收连接请求,返回一个新的对应于此次连接的套接字(accept);

5、用返回的套接字和客户端进来通信(send/recv);

6、返回。等待另一个客户请求;

7、关闭套接字(closesocket)。

客户端程序:

1、创建套接字(socket);

2、向服务器发出连接请求(connect);

3、和服务器端进行通信(send/recv);

4、关闭套接字(closesocket);

基于UDP(面向无连接)的socket编程步骤

服务器端程序:

1、创建套接字(socket);

2、将套接字绑定到一个本地地址和端口上(bind);

3、等待接收数据(recvfrom);

7、关闭套接字(closesocket)。

客户端程序:

1、创建套接字(socket);

2、向服务器发送数据(sendto);

3、关闭套接字(closesocket);

socket()函数

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

domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET(IPv4)、AF_INET6(IPv6)、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。 
type:指定socket类型。常用的socket类型有,SOCK_STREAM(流式套接字)、SOCK_DGRAM(数据报式套接字)、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等 
protocol:就是指定协议。常用的协议有,IPPROTO_TCP、PPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。

注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。对于TCP/IP协议的套接字,domain只能是AF_INET。

bind()函数

bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。

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

函数的三个参数分别为: 
sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。 
addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。

addrlen:对应的是地址的长度。 

struct sockaddr{
    sa_family_t  sin_family;   //地址族(Address Family),也就是地址类型
    char         sa_data[14];  //IP地址和端口号
};

可以用SOCKADDR_IN代替

struct sockaddr_in {
        short   sin_family;        //地址族
        u_short sin_port;          //端口
        struct  in_addr sin_addr;
        char    sin_zero[8];
};

listen()、connect()函数

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

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

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

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

accept()函数

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

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

recv()、send()等函数

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

基于TCP的socket的编程代码:

服务端TcpSrv.cpp,需要包含头文件WinSock2.h和链接ws2_32.lib

#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>
void main()
{
	//加载套接字库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return;
	}

	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			/* Tell the user that we could not find a usable */
			/* WinSock DLL.                                  */
			WSACleanup( );
			return;
	}

	SOCKET sockSrv = socket(AF_INET, SOCK_STREAM,0);//创建socket
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//除了sin_family都要使用网络字节序
	//htonl将主机的无符号长整形数转换成网络字节顺序。 
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);//需要用1024以上,将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian)
	bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));//绑定
	listen(sockSrv, 5);//监听
	SOCKADDR_IN addrClient;//保存客户端的地址
	int len = sizeof(SOCKADDR);
	while(1)
	{
		SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &len);//接受用户请求
		char sendBuf[100];
		sprintf(sendBuf, "Welcome %s to http", inet_ntoa(addrClient.sin_addr));
		send(sockConn, sendBuf, strlen(sendBuf)+1, 0);//用返回的套接字和客户端进行通信
		char recvBuf[100];
		recv(sockConn, recvBuf, 100, 0);
		printf("这是服务端:%s\n", recvBuf);
		closesocket(sockConn);//关闭套接字
	}

}

客户端TcpClient.cpp,需要包含头文件WinSock2.h和链接ws2_32.lib

#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>
void main()
{
	//加载套接字库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return;
	}

	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			/* Tell the user that we could not find a usable */
			/* WinSock DLL.                                  */
			WSACleanup( );
			return;
	}
	SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);//创建套接字

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//本地回路地址
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);//与服务端保持一致
	connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));//向服务器发送请求connect

	char recvBuf[100];
	recv(sockClient, recvBuf, 100, 0);//接受服务端的数据
	printf("这是客户端 %s\n", recvBuf);
	send(sockClient, "This is ZhangSan", strlen("This is ZhangSan")+1, 0);//向服务端发送数据
	closesocket(sockClient);
	WSACleanup();//终止对套接字库的使用
}

基于UDP的socket的编程代码:

服务端UdpSrv.cpp,需要包含头文件WinSock2.h和链接ws2_32.lib

#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>
void main()
{
	//加载套接字库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return;
	}

	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			/* Tell the user that we could not find a usable */
			/* WinSock DLL.                                  */
			WSACleanup( );
			return;
	}
	SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);

	bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);
	char recvBuf[100];
	recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &len);
	printf("%s\n", recvBuf);
	closesocket(sockSrv);
	WSACleanup();
}

客户端UdpClient.cpp,需要包含头文件WinSock2.h和链接ws2_32.lib

#include <WinSock2.h>
#include <stdlib.h>
#include <stdio.h>
void main()
{
	//加载套接字库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return;
	}

	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			/* Tell the user that we could not find a usable */
			/* WinSock DLL.                                  */
			WSACleanup( );
			return;
	}

	SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);

	sendto(sockClient, "Hello", strlen("Hello") + 1, 0,
		(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
	closesocket(sockClient);
	WSACleanup();
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值