目录
1. Socket(实现TCP)
2. 相关函数
2.1. socket()
调用socket()创建套接字
int socket(int af, int type, int protocol);
//socket()成功时返回文件描述符,失败时返回-1
-
af:套接字中使用的协议族(Protocol Family)信息
-
type:套接字数据传输类型信息
-
protocol:计算机间通信中使用的协议信息
协议族Protocol Family:
名称 | 协议族 |
PF_INET | IPv4互联网协议族 |
PF_INET6 | IPv6互联网协议族 |
PF_LOCAL | 本地通信的UNIX协议族 |
PF_PACKET | 底层套接字的协议族 |
PF_IPX | IPX Novell协议族 |
套接字类型Type:套接字类型指的是套接字的数据传输方式,通过第二个参数传递,只有这样才能决定创建的套接字的传 输方式。
套接字类型1:有保障面向连接的套接字(SOCK_STREAM)
套接字类型2:无保障面向消息的套接字(SOCK_DGRAM)
协议最终选择protocol:IPPROTO_TCP,IPPROTO_UDP
应用
int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
2.2. bind()
调用bind()函数分配IP地址和端口号
int bind (int sockfd, struct sockaddr * myaddr, socklen_t addrlen);
//成功返回0,失败返回-1
- sockfd:要分配地址信息(IP地址和端口号)的套接字文件描述符
- myaddr:存有地址信息的结构体变量地址值(struct sockaddr*)
- addrlen:第二个结构体变量的长度sizeof(myaddr)
地址信息结构体
struct sockaddr_in
{
sa_family_t sin_family; //地址族(Address Family)
uint16_t sin_port; //16位TCP/UDP端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使用
};
//sin_family(AF_INET,AF_INET16,AF_LOCAL)
//AF_INET:IPv4网络协议使用的地址族
//AF_INET16:IPv6网络协议使用的地址族
//AF_LOCAL:本地通信中采用的UNIX协议的地址族
//sin_port:保存16位端口号
//sin_addr:保存32位IP地址信息
struct in_addr
{
In_addr_t s_addr; //32位IPv4地址
};
应用:
...
struct sockaddr_in serv_addr;
char * serv_port = "9000"
//创建套接字
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
//初始化地址信息
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(serv_port));
//分配地址信息
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
...
补充:
htons():host to network short int
htonl():host to network long int
atoi():把字符串转换成整型数
2.3. listen()
调用listen()函数将套接字转换为可接收连接状态
int listen (int sockfd, int n);
//成功返回0,失败返回-1
sockfd:希望进入等待请求状态的套接字文件描述符,传递的参数成为服务器端套接字(监听套接字)。
n:连接请求等待队列(Queue)的长度,若为5,则队列长度为5,表示最多使5个连接请求进入队列
应用:
listen(serv_sock, 5);
2.4. accept()
调用accept()函数受理连接请求,如果没有连接请求的情况下调用该函数,则没有返回,直到有连接请求为止
int accept (int sockfd, struct sockaddr * addr,socklen_t * addrlen);
//成功时返回创建的套接字文件描述符,失败返回-1
- sockfd:服务器套接字的文件描述符
- addr:保存发起连接请求的客户端地址信息的变量地址值,调用函数后向传递来的地址变量地址填充客户端地址信息
- addrlen:第二个参数结构体addr的长度,但是存有长度的变量地址。函数调用后,改变量被填充为客户端地址长度
应用:
accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
2.5. write()/read()
调用write()/read()函数进行数据传输
write (int fd, const void *__buf, size_t nbytes);
//成功时返回写入的字节数,失败返回-1
//fd:显示数据传输对象的文件描述符
//buf:保存要传输数据的缓冲地址池
//nbytes:要传输数据的字节数
read (int fd, void *__buf, size_t nbytes);
//成功时返回接收到的字节数(若遇到文件结尾返回0),失败返回-1
//fd:显示数据接收对象的文件描述符
//buf:要保存接收数据的缓冲地址值
//nbytes:要接收数据的字节数
2.6. connect()
调用connect()函数向服务器发送连接请求
int connect (int sockfd,struct sockaddr* servaddr, socklen_t addrlen);
//成功返回0,失败返回-1
- sockfd:客户端套接字文件描述符
- servaddr:保存目标服务器地址信息的变量地址值
- addrlen:以字节为单位传递已传递给第二个结构体参数的地址变量长度
应用:
connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr);
3. 代码
3.1. 服务端代码
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int serv_sock;
int clnt_sock;
struct sockaddr_in serv_addr;
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size;
string message = "Hello World!";
if (argc != 2) {
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
//1.1. 调用socket()创建套接字
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if (serv_sock == -1)
cout << "socket() error" << endl;
//地址初始化
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
//1.2. 调用bind()函数分配IP地址和端口号
if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
cout << "bind() error" << endl;
//1.3. 调用listen()函数将套接字转换为可接收连接状态
if (listen(serv_sock, 5) == -1)
cout << "listen() error" << endl;
clnt_addr_size = sizeof(clnt_addr);
//1.4. 调用accept()函数受理连接请求
clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
if (clnt_sock == -1)
cout << "accept() error" << endl;
//1.5. 调用write()/read()函数进行数据传输
write(clnt_sock,message.c_str(), sizeof(message));
close(clnt_sock);
close(serv_sock);
return 0;
}
3.2. 客户端代码
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
char message[30];
int str_len = 0;
int idx = 0, read_len = 0;
if (argc != 3) {
cout << "Usage : %s IP port" << argv[0] << endl;
exit(1);
}
//2.1. 调用socket()创建套接字
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1)
cout << "socket() error" << endl;
//地址初始化
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
serv_addr.sin_port = htons(atoi(argv[2]));
//2.2.调用connect()函数向服务器发送连接请求
if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1)
cout << "connect() error!" << endl;
//2.3. 调用write()/read()函数进行数据传输
while (read_len = read(sock, &message[idx++], 1))
{
if (read_len == -1)
cout << "read() error!" << endl;
str_len += read_len;
}
cout << "Message from server: " << message << endl;
cout << "Function read call count: " << str_len << endl;
close(sock);
return 0;
}