c++的socket编程 tcp
首先看一下要用到的一些函数
1、创建套接字 socket()
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain:
Name Purpose Man page AF_UNIX, AF_LOCAL Local communication unix(7) AF_INET IPv4 Internet protocols ip(7) AF_INET6 IPv6 Internet protocols ipv6(7) AF_IPX IPX - Novell protocols AF_NETLINK Kernel user interface device netlink(7) AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7) AF_AX25 Amateur radio AX.25 protocol AF_ATMPVC Access to raw ATM PVCs AF_APPLETALK AppleTalk ddp(7) AF_PACKET Low level packet interface packet(7) AF_ALG Interface to kernel crypto API
type: 连接类型
SOCK_STREAM Provides sequenced, reliable, two-way, connection- based byte streams. An out-of-band data transmission mechanism may be supported. SOCK_DGRAM Supports datagrams (connectionless, unreliable mes‐ sages of a fixed maximum length). SOCK_SEQPACKET Provides a sequenced, reliable, two-way connection- based data transmission path for datagrams of fixed maximum length; a consumer is required to read an entire packet with each input system call. SOCK_RAW Provides raw network protocol access. SOCK_RDM Provides a reliable datagram layer that does not guar‐ antee ordering. SOCK_PACKET Obsolete and should not be used in new programs; see packet(7).
protocol 协议类型
socket编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW),前两者较常用。基于TCP的socket编程是流式套接字。
2、将socket绑定到地址 bind()
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd 是在上个函数socket()中获得的文件描述符
addr 结构体类型指针 指向地址
struct sockaddr { sa_family_t sa_family; char sa_data[14]; }
addrlen 长度,是前面那个参数 addr 的长度,一般用sizeof求得
struct sockaddr_in { short int sin_family; /* 通信类型 */ unsigned short int sin_port; /* 端口 */ struct in_addr sin_addr; /* Internet 地址 */ unsigned char sin_zero[8]; /* 与sockaddr结构的长度相同*/ };
struct in_addr { in_addr_t s_addr; };
这里使用sockaddr_in 结构体 传参的时候在强制转换为 sockaddr 结构体
sockaddr_in 是专门用来保存ipv4的结构体 保存ipv6要用 sockaddr_in6
sin_port 用htons()函数 将主机字节序转化为网络字节序
3、监听模式等待请求 linsten()
int listen(int sockfd, int backlog);
sockfd 文件描述符
backlog 等待连接队列的最大长度
4、向服务器发出连接请求 connect()
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd 文件描述符
addr 结构体类型指针 指向服务器地址
addrlen 结构体长度
5、服务器接收客户端的连接 accpet()
int accept(int socket,sockaddr * fromaddr,int * addrlen);
socket 文件描述符
fromaddr 客户端地址信息
addrlen 地址结构体长度
6、发送数据函数 send()
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
sockfd 文件描述符
*buf 发送的数据
len 发送数据长度
flags 0
7、接收数据函数 recv()
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd 文件描述符
*buf 接收的数据
len 接收数据长度
flags 0
8、数据的读写 readI() write()
ssize_t read(int fd, void *buf, size_t nbytes);
ssize_t write(int fd, const void *buf, size_t nbytes);
fd 文件描述符
buf 读写的数据存放指针
nbytes 写入读出的数据字节数
9、关闭套接字 closesocket()
int closesocket(int socket);
linux:
服务器:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> int main(){ //创建套接字 int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //将套接字和IP、端口绑定 struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充 serv_addr.sin_family = AF_INET; //使用IPv4地址 serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址 serv_addr.sin_port = htons(1234); //端口 bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); //进入监听状态,等待用户发起请求 listen(serv_sock, 20); //接收客户端请求 struct sockaddr_in clnt_addr; socklen_t clnt_addr_size = sizeof(clnt_addr); int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); //向客户端发送数据 char str[] = "hello world"; write(clnt_sock, str, sizeof(str)); //关闭套接字 close(clnt_sock); close(serv_sock); return 0; }
客户端:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> int main(){ //创建套接字 int sock = socket(AF_INET, SOCK_STREAM, 0); //向服务器(特定的IP和端口)发起请求 struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充 serv_addr.sin_family = AF_INET; //使用IPv4地址 serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址 serv_addr.sin_port = htons(1234); //端口 connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); //读取服务器传回的数据 char buffer[40]; read(sock, buffer, sizeof(buffer)-1); printf("Message form server: %s\n", buffer); //关闭套接字 close(sock); return 0; }
windows:
服务器:
#include <stdio.h> #include <winsock2.h> #pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll int main(){ //初始化 DLL WSADATA wsaData; WSAStartup( MAKEWORD(2, 2), &wsaData); //创建套接字 SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //绑定套接字 sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充 sockAddr.sin_family = PF_INET; //使用IPv4地址 sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址 sockAddr.sin_port = htons(1234); //端口 bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //进入监听状态 listen(servSock, 20); //接收客户端请求 SOCKADDR clntAddr; int nSize = sizeof(SOCKADDR); SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize); //向客户端发送数据 char *str = "Hello World!"; send(clntSock, str, strlen(str)+sizeof(char), NULL); //关闭套接字 closesocket(clntSock); closesocket(servSock); //终止 DLL 的使用 WSACleanup(); return 0; }
客户端:
#include <stdio.h> #include <stdlib.h> #include <WinSock2.h> #pragma comment(lib, "ws2_32.lib") //加载 ws2_32.dll int main(){ //初始化DLL WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); //创建套接字 SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //向服务器发起请求 sockaddr_in sockAddr; memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充 sockAddr.sin_family = PF_INET; sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); sockAddr.sin_port = htons(1234); connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)); //接收服务器传回的数据 char szBuffer[MAXBYTE] = {0}; recv(sock, szBuffer, MAXBYTE, NULL); //输出接收到的数据 printf("Message form server: %s\n", szBuffer); //关闭套接字 closesocket(sock); //终止使用 DLL WSACleanup(); system("pause"); return 0; }
nbytes