为了执行网络输入输出,一个进程必须作的第一件事就是调用socket函数得到一个文件描述符。windows
int socket(int family,int type,int protocol); //失败返回-1,并设置errno为相应的值,其它值为成功
第一个参数指明网络层协议簇,目前支持5种,最经常使用的有AF_INET(IPv4协议)和AF_INET6(IPv6协议);服务器
第二个参数指明套接口类型,有四种类型可选:SOCK_STREAM(字节流套接口)、SOCK_DGRAM(数据报套接口)、SOCK_SEQPACKET(有序分组)和SOCK_RAW(原始套接口)。SOCK_STREAM基于TCP,是有保障的、面向链接的SOCKET,多用于资料(如文件)传送;SOCK_DGRAM基于UDP,是无保障的面向消息的socket , 主要用于在网络上发广播信息。网络
第三个参数指明相应的传输协议,常见的协议有TCP、UDP、SCTP,要指定它们分别使用宏IPPROTO_TCP、IPPROTO_UPD、IPPROTO_SCTP来指定,若是该值为0则使用默认值。socket
这些地址结构的名字均已“sockaddr_”开头,并以对应每一个协议族的惟一后缀结束。以IPv4套接口地址结构为例,它以“sockaddr_in”命名,如下是结构体的内容:tcp
struct in_addr { in_addr_t s_addr; /* IPv4地址 */ }; struct sockaddr_in { uint8_t sin_len; /* 无符号的8位整数 */ sa_family_t sin_family; /* 套接口地址结构的地址簇,这里为AF_INET */ in_port_t sin_port; /* TCP或UDP端口 */ struct in_addr sin_addr; char sin_zero[8]; };
(1) setsockopt()函数,用于任意类型、任意状态套接口的设置选项值。函数
int setsockopt(int scokfd,int level,int name,char *value,int *optlen);
(2) getsockopt()用于获取任意类型、任意状态套接口的选项当前值,并把结果存入optval。ui
int getsockopt(int sockfd,int level,int name,char *value,int optlen);
这两个函数参数相同以下:url
第一个参数sockfd是套接字描述符,对于服务器是accept()函数返回的已链接套接字描述符,对于客户端是调用socket()函数返回的套接字描述符spa
第二个参数level是选项定义的层次,支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6设计
第三个参数optname是需设置的选项
第四个参数optval是指针,指向存放选项待设置的新值的缓冲区
第五个参数optlen是optval缓冲区长度
为套接口分配一个本地IP和协议端口,对于网际协议,协议地址是32位IPv4地址或128位IPv6地址与16位的TCP或UDP端口号的组合;如指定端口为0,调用bind时内核将选择一个临时端口,若是指定一个通配IP地址,则要等到创建链接后内核才选择一个本地IP地址。
int bind(int sockfd, const struct sockaddr * server, socklen_t addrlen); //返回0表示成功,-1表示失败
第一个参数是socket函数返回的套接口描述字;
第二个和第三个参数分别是一个指向特定于协议的地址结构的指针和该地址结构的长度。
listen函数仅被TCP服务器调用,它的做用是将用sock建立的主动套接口转换成被动套接口,并等待来自客户端的链接请求。
int listen(int sockfd,int backlog); //返回0表示成功,返回-1表示失败
第一个参数是socket函数返回的套接口描述字;
第二个参数规定了内核为此套接口排队的最大链接个数。因为listen函数第二个参数的缘由,内核要维护两个队列:已完成链接队列和未完成链接队列。未完成队列中存放的是TCP链接的三路握手未完成的链接,accept函数是从已完成队列中取链接返回给进程;当已完成队列为空时,进程将进入睡眠状态。
accept函数由TCP服务器调用,从已完成链接队列头返回一个已完成链接,若是完成链接队列为空,则进程进入睡眠状态。
int accept(int listenfd, struct sockaddr *client, socklen_t * addrlen); //返回值非负表示成功,-1表示失败
第一个参数是socket函数返回的套接口描述字;第二个和第三个参数分别是一个指向链接方的套接口地址结构和该地址结构的长度;该函数返回的是一个全新的套接口描述字;若是对客户段的信息不感兴趣,能够将第二和第三个参数置为空。
当服务器和客户端的链接创建起来后,就能够进行数据传输了,服务器和客户端用各自的套接字描述符进行读/写操做。由于套接字描述符也是一种文件描述符,因此能够用文件读/写函数write()和read()进行接收和发送操做。
(1) write()函数用于数据的发送。
int write(int sockfd, char *buf, int len); //返回值非负表示成功,-1表示失败
(2) read()函数用于数据的接收。
int read(int sockfd, char *buf, int len); //返回值非负表示成功,-1表示失败
两个函数参数相同以下:
第一个参数sockfd是套接字描述符,对于服务器是accept()函数返回的已链接套接字描述符,对于客户端是调用socket()函数返回的套接字描述符;
第二个参数buf是指向一个用于发送 / 接收信息的数据缓冲区;
第三个参数len指明发送 / 接收数据缓冲区的大小。
TCP套接字提供了send()和recv()函数,用来发送和接收操做。这两个函数与write()和read()函数很类似,只是多了一个附加的参数。
(1) send()函数用于数据的发送。
ssize_t send(int sockfd, const void *buf, size_t len, int flags); //成功返回写出的字节数,失败返回-1
前3个参数与write()相同,参数flags是传输控制标志。
(2) recv()函数用于数据的发送。
ssize_t recv(int sockfd, void *buf, size_t len, int flags); //成功返回读入的字节数,失败返回-1
前3个参数与read()相同,参数flags是传输控制标志。
TCP服务器端:Windows和Linux皆可
// ServerMain.cpp : Linux/windows Server // socket()->bind()->listen()->accept()->read()->write()->close() #include <stdint.h> #include <stdio.h> #ifdef _MSC_VER #include <winsock2.h> #include <ws2tcpip.h> #pragma comment(lib, "Ws2_32.lib") #else #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <arpa/inet.h> #include <unistd.h> #include <cstring> #endif bool CloseSerSocket(uint32_t dwSockId,const char* strError) { #ifdef _MSC_VER closesocket(dwSockId); #else close(dwSockId); #endif if (NULL != strError) { perror(strError); return false; } else { return true; } } int main() { #ifdef _MSC_VER WORD wVersion; WSADATA wsData; wVersion = MAKEWORD(1, 1); if (WSAStartup(wVersion, &wsData) != 0) { perror("windows network init error"); return false; } int nOn = 1000; #else struct timeval nOn = { 1,0 }; #endif // 初始化Socket uint32_t dwSocketId = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (dwSocketId == -1) { return CloseSerSocket(dwSocketId, "init socket error, can not get file description"); } // 设置地址重用 if (setsockopt(dwSocketId, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOn, sizeof(nOn)) == -1) { return CloseSerSocket(dwSocketId, "set socket error"); } // 设置服务器信息 sockaddr_in stServerAddr; sockaddr_in stClientAddr; stServerAddr.sin_family = AF_INET; stServerAddr.sin_port = htons(5050); stServerAddr.sin_addr.s_addr = htonl(INADDR_ANY); //INADDR_ANY就是inet_addr("0.0.0.0") 当服务器的监听地址是INADDR_ANY时,就是全部的都监听 // 绑定socket if (bind(dwSocketId,(const sockaddr*)&stServerAddr,sizeof(stServerAddr)) == -1) { return CloseSerSocket(dwSocketId, "bind socket error"); } // 监听客户端请求 if (listen(dwSocketId, 100) == -1) { return CloseSerSocket(dwSocketId, "listen socket error"); } socklen_t nCliSize = 0; uint32_t dwSocketFd = 0; char reveBuff[2048]; char sendBuff[2048]; while (true) { memset(reveBuff, '\0', sizeof(reveBuff)); memset(sendBuff, '\0', sizeof(sendBuff)); nCliSize = sizeof(stClientAddr); // 从已完成链接队列头返回一个已完成链接 if ((dwSocketFd = accept(dwSocketId, (sockaddr*)&stClientAddr, &nCliSize)) == -1) { return CloseSerSocket(dwSocketId, "accept socket error"); } // 接收客户端包 if (recv(dwSocketFd, reveBuff, 2048, 0) == -1) { return CloseSerSocket(dwSocketId, "recv package error"); } sprintf(sendBuff, "i have receive the message:%s from you(%s)", reveBuff, inet_ntoa(stClientAddr.sin_addr)); // 返回客户包 if (send(dwSocketFd, sendBuff, strlen(sendBuff), 0) == -1) { return CloseSerSocket(dwSocketId, "send package error"); } } CloseSerSocket(dwSocketId, NULL); return 0; }
TCP客户端:仅限Windows端
// ClientMain.cpp : Windows Only #pragma once #include <stdint.h> #include <stdio.h> #include <winsock2.h> #include <ws2tcpip.h> #pragma comment(lib, "Ws2_32.lib") bool CloseCliSocket(uint32_t dwSockId, const char* strError) { closesocket(dwSockId); if (NULL != strError) { perror(strError); system("pause"); return false; } else { return true; } } int main() { WORD wVersion; WSADATA wsData; wVersion = MAKEWORD(1, 1); if (WSAStartup(wVersion,&wsData) != 0) { perror("windows network init error"); return false; } // 初始化Socket uint32_t dwSocketId = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (dwSocketId == -1) { return CloseCliSocket(dwSocketId, "init socket error, can not get file description"); } // 设置服务器信息 sockaddr_in stServerAddr; stServerAddr.sin_family = AF_INET; stServerAddr.sin_port = htons(5050); stServerAddr.sin_addr.s_addr = inet_addr("192.168.1.5"); if (connect(dwSocketId, (const sockaddr*)&stServerAddr, sizeof(stServerAddr)) == -1) { return CloseCliSocket(dwSocketId, "connect server error"); } char delBuff[2048]; sprintf(delBuff, "%s", "hello"); // 发送包 if (send(dwSocketId, delBuff, strlen(delBuff), 0) == -1) { return CloseCliSocket(dwSocketId, "send package error"); } memset(delBuff, '\0', sizeof(delBuff)); // 接收包 if (recv(dwSocketId, delBuff, 2048, 0) == -1) { return CloseCliSocket(dwSocketId, "recv package error"); } printf("%s\n", delBuff); CloseCliSocket(dwSocketId, NULL); system("pause"); return 0; }