简介
在进行socket网络编程之前,我们需要知道socket是什么?在Unix/linux操作系统中有一个哲学思想,那就是万物皆文件,socket也不例外,它是可读、可写、可控制、可关闭的文件描述符。使用socket网络编程实现服务端和客户端之间的通信步骤可由下图结构所示:
服务端和客户端
1.创建socket
int socket(int domain, int type, int protocol);
domain:系统使用的底层协议族。PF_INET(用于IPv4),PF_INET6(用于IPv6),PF_UNIX(用于UNIX);
type:指定服务类型。SOCK_STREAM(流服务,表示传输层使用TCP),SOCK_UGRAM(数据报,表示传输层使用UDP);
protocol:前两个参数构成的协议集合下,再选择一个具体的协议,一般都设置为0;
2.绑定socket
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen指出该socket地址的长度 成功时返回1,失败返回-1并设置errno;
3.监听socket
int listen(int sockfd, int backlog);
sockfd指被监听的socket,backlog提示内核监听队列的最大长度 成功时返回0,失败返回-1并设置errno;
4.接受连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd是执行过listen系统调用的监听socket,addr是用来获取被接受连接的远端socket地址,该地址的长度由addrlen参数指出
成功时返回一个新的连接socket,该socket唯一地标识了被接受的这个连接,失败时返回-1并设置errno;
5.发起连接
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
sockfd由socket 系统调用返回一个socket。serv_addr是服务器监听的socket地址,addrlen则指定这个地址的长度 成功时放回0;一旦成功,sockfd就唯一地标识了这个连接,客户端就可以通过读写sockfd来与服务器通信。失败放回-1并设置errno;
6.通信
TCP数据读写
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
recv读取sockfd上的数据,buf和len分别指定读缓冲区的位置和大小,flags通常为0。成功时返回实际读取到的数据长度,它可能小于期望的长度,因此需要多次调用recv;出错时返回-1并设置errno;
send往sockfd上写入数据,buf和len分别指定写缓冲区的位置和大小。成功时返回实际写入的数据长度,失败则返回-1并设置errno;
7.关闭连接
int close(int fd);
fd是待关闭的socket;
服务端参考代码
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
using namespace std;
int main(int argc, char* argv[])
{
if(argc < 3)
{
printf(" Usage: %s <ip of server> <port of server>\n", argv[0]);
return -1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
printf("创建SOCKET失败\n");
return -1;
}
printf("创建SOCKET成功!\n");
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
// inet_pton(AF_INET, ip, &serv_addr.sin_addr);
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = htons(INADDR_ANY);
//serv_addr.sin_port = htons(12345);
//serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("绑定失败!\n");
return -1;
}
printf("绑定成功!\n");
if(listen(sockfd, 10) < 0)
{
printf("监听失败!\n");
return -1;
}
printf("监听成功!\n");
struct sockaddr_in client_addr;
socklen_t client_addrlength = sizeof(client_addr);
int client;
client = accept(sockfd, (struct sockaddr*)&client_addr, &client_addrlength);
if(client < 0)
{
printf("接受失败!\n");
return -1;
}
char remote[INET_ADDRSTRLEN];
// printf("%s连接成功!\n", inet_ntop(AF_INET, &client_addr.sin_addr, remote, INET_ADDRSTRLEN));
printf("有客户端连接进来了:%s\n", inet_ntoa(client_addr.sin_addr));
char recvBuff[255];
while(1)
{
bzero(recvBuff, sizeof(recvBuff));
int n = recv(client, recvBuff, sizeof(recvBuff)-1, NULL);
if(n > 0)
{
//recvBuff[n] = '\0';
printf("接收到%d字节\n", n);
printf(">> %s\n", recvBuff);
}
}
while(1);
return 0;
}
客户端参考代码
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
using namespace std;
int main(int argc, char* argv[])
{
if(argc < 3)
{
printf(" Usage: %s <ip of server> <port of server>\n", argv[0]);
return -1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
printf("创建SOCKET失败\n");
return -1;
}
printf("创建SOCKET成功!\n");
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
//inet_pton(AF_INET, ip, &serv_addr.sin_addr);
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = htons(INADDR_ANY);
//serv_addr.sin_port = htons(12345);
//serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)))
{
printf("连接失败!\n");
return -1;
}
printf("连接成功!\n");
int r;
char sendBuff[255];
while (1)
{
memset(sendBuff, 0, 255);
printf("你要发啥:");
//scanf("%s", sendBuff);
gets(sendBuff);
r = send(sockfd, sendBuff, strlen(sendBuff), NULL);
if (r > 0)
{
printf(">> 发送%d字节到服务器成功\n", r);
}
}
return 0;
}
对代码进行编译
g++ server.cpp -o server
g++ client.cpp -o client
然后运行
总结
- socket->bind->listen->accept->connect->read->write->close构成一个TCP服务端
- socket->connect->read->write->close构成一个TCP客户端
查找结构体方法
-nir中 n表示显示行号,i表示不区分大小写,r表示逐行扫描