Linux网络编程实质上就是socket编程,IP地址可以确定一个主机,端口号可以确定主机中的一个进程,socket = IP + port(端口号)
这样便确定了主机中运行的进程,网络通信实质上就是进程通信。
我们知道连接分为两种,可靠的TCP连接与不可靠的UDP连接
,下面分别通过这两种连接来熟悉socket编程.
socket -> bind -> listen -> while(1){ accept -> doing ...} -> close
socket -> connect -> while (1) {doing ...} ->close
int socket(int __domain, int __type, int __protocol)
服务端建立socket。
int bind(int __fd, const sockaddr *__addr, socklen_t __len)
将已经创建好的socket进行绑定好 IP地址,端口号等,如果想要进行网络通信,就需要通过这个socket。
int listen(int __fd, int __n)
在绑定好socket之后,开始进行监听,注意监听是非阻塞,只是说明服务端开始监听socket
int accept(int __fd, sockaddr *__restrict__ __addr, socklen_t *__restrict__ __addr_len)
accept函数一般是阻塞状态,用于接收连接的服务器,注意返回值是返回一个新的 newfd, 之后与服务器与客户端通信就是通过这个套接字进行通信
注意:此时sockfd只是用于接收客户端连接成功后的newfd套接字, 之后的通信都将在套接字newfd中进行, 也就是说,sockfd并不参与数据的交换。
close
close掉所有的newfd,sockfd
int socket(int __domain, int __type, int __protocol)
int connect(int __fd, const sockaddr *__addr, socklen_t __len)
这个connect与服务端的bind函数相对应,连接上服务端已经绑定好的socket
注意,在bind之前可能会遇到:bind: Address already in use 这种情况
出现的原因是绑定的套接字在close之后还需要等待TIME_WAIT一段时间才能够进行重新绑定。
此时我们可以使用以下语句,让套接字可以立刻再次使用:
int on = 1;
ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
ERROR_CHECK(ret, -1, "setsockopt");
server
:
int main()
{
int ret;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
ERROR_CHECK(sockfd, -1, "socket");
int on = 1;
ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
ERROR_CHECK(ret, -1, "setsockopt");
struct sockaddr_in sockinfo;
bzero(&sockinfo, sizeof(sockinfo));
sockinfo.sin_addr.s_addr = inet_addr("172.21.0.7");
sockinfo.sin_port = htons(2000);
sockinfo.sin_family = AF_INET;
ret = bind(sockfd, (sockaddr*)&sockinfo, sizeof(sockinfo));
ERROR_CHECK(ret, -1, "bind");
ret = listen(sockfd, 10);
ERROR_CHECK(ret, -1, "listen");
sockaddr_in client;
bzero(&client, sizeof(client));
socklen_t len = sizeof(sockaddr);
int newfd = ::accept(_socket.getFd(), (sockaddr *)&client, &len);
ERROR_CHECK(newfd, -1, "accept");
std::cout << "client " << inet_ntoa(client.sin_addr) << ":" << ntohs(client.sin_port) << " has connected" << std::endl;
char buf[100] = {0};
recv(newfd, buf, sizeof(buf), 0);
cout << "receiving from client:" << buf << endl;
send(newfd, "world", 5, 0);
close(sockfd);
close(newfd);
return 0;
}
client
:
int main()
{
int ret;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
ERROR_CHECK(sockfd, -1, "socket");
struct sockaddr_in sockinfo;
bzero(&sockinfo, sizeof(sockinfo));
sockinfo.sin_addr.s_addr = inet_addr("172.21.0.7");
sockinfo.sin_port = htons(2000);
sockinfo.sin_family = AF_INET;
ret = connect(sockfd, (sockaddr *)&sockinfo, sizeof(sockinfo));
ERROR_CHECK(ret, -1, "connect");
send(sockfd, "hello", 5, 0);
char buf[100] = {0};
recv(sockfd, buf, sizeof(buf), 0);
cout << "receiving from server:" << buf << endl;
close(sockfd);
return 0;
}
udp是不可靠的连接方法,所以客户端的流程与服务端的流程都比较简单
服务端:socket -> bind -> recvfrom/sendto
客户端:socket -> recvfrom/sendto
主要掌握以下两个函数:
recvfrom(..., sockaddr *__restrict__ __addr, socklen_t *__restrict__ __addr_len)
前面参数与recv 相同,addr结构体用于存放发送方的IP地址与端口号。
sendto(..., const sockaddr *__addr, socklen_t __addr_len)
前面的参数与send相同,addr结构体用于存放目标地址IP与端口号
server
:
int main()
{
int ret;
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
ERROR_CHECK(sockfd, -1, "socket");
struct sockaddr_in sockinfo;
socklen_t len = sizeof(sockinfo);
bzero(&sockinfo, sizeof(sockinfo));
sockinfo.sin_addr.s_addr = inet_addr("172.21.0.7");
sockinfo.sin_port = htons(2012);
sockinfo.sin_family = AF_INET;
ret = bind(sockfd, (sockaddr *)&sockinfo, len);
ERROR_CHECK(ret, -1, "bind");
char buf[100] = {0};
struct sockaddr_in client;
recvfrom(sockfd, buf, sizeof(buf), 0, (sockaddr*)&client, &len);
cout << " client " << inet_ntoa(client.sin_addr) << " :" << ntohs(client.sin_port) << endl;
cout << "receiving :" << buf << endl;
sendto(sockfd, "world", 5, 0, (sockaddr*)&sockinfo, len);
close(sockfd);
return 0;
}
client
:
int main()
{
int ret;
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
ERROR_CHECK(sockfd, -1, "socket");
struct sockaddr_in sockinfo;
socklen_t len = sizeof(sockinfo);
bzero(&sockinfo, sizeof(sockinfo));
sockinfo.sin_addr.s_addr = inet_addr("172.21.0.7");
sockinfo.sin_port = htons(2012);
sockinfo.sin_family = AF_INET;
sendto(sockfd, "hello", 5, 0, (sockaddr*)&sockinfo, len);
char buf[100] = {0};
recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
cout << "receving from server:" << buf << endl;
close(sockfd);
return 0;
}