【Linux网络编程】socket详解

简单的socket通信

#include <sys/socket.h>
int sockfd = socket(AF_INET, SOCK_STREAM, 0);

第一个参数:IP地址类型,AF_INET表示使用IPv4,如果使用IPv6请使用AF_INET6。
第二个参数:数据传输方式,SOCK_STREAM表示流格式、面向连接,多用于TCP。SOCK_DGRAM表示数据报格式、无连接,多用于UDP。
第三个参数:协议,0表示根据前面的两个参数自动推导协议类型。设置为IPPROTO_TCP和IPPTOTO_UDP,分别表示TCP和UDP。
对于客户端,服务器存在的唯一标识是一个IP地址和端口,这时候我们需要将这个套接字绑定到一个IP地址和端口上。首先创建一个sockaddr_in结构体

#include <arpa/inet.h>  //这个头文件包含了<netinet/in.h>,不用再次包含了
struct sockaddr_in serv_addr;
bzero(&serv_addr, sizeof(serv_addr));

然后使用bzero初始化这个结构体,这个函数在头文件<string.h>或中。这里用到了两条《Effective C++》的准则:
条款04: 确定对象被使用前已先被初始化。如果不清空,使用gdb调试器查看addr内的变量,会是一些随机值,未来可能会导致意想不到的问题。
条款01: 视C++为一个语言联邦。把C和C++看作两种语言,写代码时需要清楚地知道自己在写C还是C++。如果在写C,请包含头文件<string.h>。如果在写C++,请包含<cstring> 。

设置地址族、IP地址和端口:
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
serv_addr.sin_port = htons(8888);
然后将socket地址与文件描述符绑定:
bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr));

为什么定义的时候使用专用socket地址(sockaddr_in)而绑定的时候要转化为通用socket地址(sockaddr),以及转化IP地址和端口号为网络字节序的inet_addr和htons等函数及其必要性
在游双《Linux高性能服务器编程》第五章第一节:socket地址API中有详细讨论。
在这里插入图片描述
最后我们需要使用listen函数监听这个socket端口,这个函数的第二个参数是listen函数的最大监听队列长度,系统建议的最大值SOMAXCONN被定义为128。
listen(sockfd, SOMAXCONN);
要接受一个客户端连接,需要使用accept函数。对于每一个客户端,我们在接受连接时也需要保存客户端的socket地址信息,于是有以下代码:

struct sockaddr_in clnt_addr;
socklen_t clnt_addr_len = sizeof(clnt_addr);
bzero(&clnt_addr, sizeof(clnt_addr));
int clnt_sockfd = accept(sockfd, (sockaddr*)&clnt_addr, &clnt_addr_len);
printf("new client fd %d! IP: %s Port: %d\n", clnt_sockfd, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port));

要注意和accept和bind的第三个参数有一点区别,对于bind只需要传入serv_addr的大小即可,而accept需要写入客户端socket长度,所以需要定义一个类型为socklen_t的变量,并传入这个变量的地址。另外,accept函数会阻塞当前程序,直到有一个客户端socket被接受后程序才会往下运行。

代码:https://github.com/poppynull/WebServer

猜你喜欢

转载自blog.csdn.net/poppyty/article/details/129375667