本文在gnu/linux环境编程一书的基础上进行补充和扩展。
先看server端的代码部分:
int main ( void )
{
int serverFd, connectionFd;
struct sockaddr_in servaddr;
char timebuffer[MAX_BUFFER+1];
time_t currentTime;
serverFd = socket( AF_INET, SOCK_STREAM, 0 );
memset( &servaddr, 0, sizeof(servaddr) );
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(DAYTIME_SERVER_PORT);
bind( serverFd,
(struct sockaddr *)&servaddr, sizeof(servaddr) );
listen( serverFd, 5 );
while ( 1 ) {
connectionFd = accept( serverFd,
(struct sockaddr *)NULL, NULL );
if (connectionFd >= 0) {
currentTime = time(NULL);
snprintf( timebuffer, MAX_BUFFER,
"%s\n", ctime(¤tTime) );
write( connectionFd, timebuffer, strlen(timebuffer) );
close( connectionFd );
}
}
}
宏 DAYTIME_SERVER_PORT 为 13,此为知名端口,需在执行server时添加sudo。
第一步:创建socket(相当于在协议栈内部开辟一块内存空间,用于存放控制信息)
网络地址族 AF_INET,表明要使用ipv4网络协议。 AF全称为address framily地址族,貌似和PF(也就是protocol family协议族)差不多。参考https://www.jianshu.com/p/3b10c4ec6d8e
第二个参数定义通信的语法,SOCK_STREAM类型对应流类型的通信(TCP),这个最常用。
第二步:绑定
初始化需要绑定的socket控制信息(地址族+IP地址+端口)
INADDR_ANY代表允许接收所有地址的请求消息,也可用inet_addr("x.x.x.x")来指定服务器某个网卡的ip,在ch13_socket_amend/文件夹中有指定服务器的地址为127.0.0.110,你也可以自个替换,不过得确保和客户端请求的地址一致。
htonl函数将一个32位地址数从主机字节顺序转换成网络字节顺序。(host to net long int)
htons函数将一个16位数从主机字节顺序转换成网络字节顺序。参考https://blog.csdn.net/haoxiaodao/article/details/73162663
第三步:监听
第四步:调用accept进入阻塞,等待客户端连接,连接后获取客户端的ip及port。
客户端部分代码:
connectionFd = socket(AF_INET, SOCK_STREAM, 0);
memset(&cliaddr, 0, sizeof(cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_port = htons(DAYTIME_CLIENT_PORT);
cliaddr.sin_addr.s_addr = inet_addr("127.0.0.123");//client ip
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(DAYTIME_SERVER_PORT);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.110");//server ip
bind(connectionFd,(struct sockaddr *)&cliaddr, sizeof(cliaddr) );
connectValue=connect(connectionFd,(struct sockaddr *)&servaddr, sizeof(servaddr));
printf("connect %d\n",connectValue);
if(connectValue==0)//check if connectted.
{
int ret=0,size;
size = sizeof(tmpaddr);
ret=getsockname(connectionFd,(struct sockaddr *)&tmpaddr,&size );
if(ret>=0){
printf("client ip:%s, port:%d\n",inet_ntoa(tmpaddr.sin_addr),ntohs(tmpaddr.sin_port));
}
}
while ( (in = read(connectionFd, &timebuffer[index], limit)) > 0) {
index += in;
limit -= in;
}
timebuffer[index] = 0;
printf("\n%s\n", timebuffer);
调用connect()函数时进程会进入阻塞态,向服务端发送请求,通过三次握手来建立TCP连接。
connect()函数在第二次握手时会有返回值,accept()在第三次时有返回值。
getsockname() 函数能够获取指定套接口描述符的控制信息,需要在connect连接成功后获取。客户端如果没用bind绑定指定ip和端口,系统会自动分配,服务器如果使用INADDR_ANY做为绑定的ip地址,则获取到的ip值为0.0.0.0
用ch13_socket_amend 文件夹里的代码编译运行后的log:
持续更新ing