service:
/*
* 抄自《UNIX网络编程:卷1》, 稍作修改。
* 仅仅用于学习目的。学无止境,进步每一天。
*
* slickedit编辑。
*
* 254008829@qq.com
*
*/
#include <stdio.h>
#include <stdlib.h> /* exit */
#include <string.h> /* bzero */
#include <unistd.h> /* read */
#include <netinet/in.h> /* strut sockaddr_in */
#include <arpa/inet.h> /* inet_pton */
#include <time.h>
#include <errno.h> /* errno */
#define MAXLINE 65507
#define LISTEN_MAX 10
#define FUNC_RET(func, val) \
do { \
int rv; \
if ( (rv=func) < 0) { \
printf("LINE = %d, error str: %s\n", __LINE__, strerror(errno)); \
exit(-1); \
} \
val = rv; \
} while (0);
int
main(int argc, char **argv)
{
int sockfd, connfd, ret;
char buf[MAXLINE];
struct sockaddr_in servaddr;
FUNC_RET(socket(AF_INET, SOCK_STREAM, 0), sockfd);
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(13);
/* INADDR_ANY,当服务器有多个网络接口的时候, 服务器可以从任意网络接口上接收来自客服端的消息 */
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/* 将生成的sockfd与设置的servaddr绑定 */
FUNC_RET(bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)), ret);
/* listen把该套接字转化成一个监听套接字 */
FUNC_RET(listen(sockfd, LISTEN_MAX), ret);
while (1) {
time_t ticks;
/* accept函数执行后服务进程会陷入睡眠,等待某个客户端连接,并被内核接受, 唤醒进程。
TCP连接使用的是三次握手, 握手完成后accept返回, 它返回的是新的描述符。称之为已连接描述符,
服务端用这个新的connfd与新近连接的那个客户程序通信 */
FUNC_RET(accept(sockfd, (struct sockaddr *)NULL, NULL), connfd);
ticks = time(NULL);
snprintf(buf, sizeof(buf), "%.24s\r\n", ctime(&ticks));
write(connfd, buf, strlen(buf));
close(connfd);
}
exit(0);
}
/*
在ubuntu下编译: gcc daytimetcpserv.c -o service
运行: sudo ./service
*/
client:
/*
* 抄自《UNIX网络编程:卷1》
* 仅仅用于学习目的
*
* slickedit编辑。
*
* 254008829@qq.com
*
*/
#include <stdio.h>
#include <stdlib.h> /* exit */
#include <string.h> /* bzero */
#include <unistd.h> /* read */
#include <netinet/in.h> /* strut sockaddr_in */
#include <arpa/inet.h> /* inet_pton */
#include <errno.h> /* errno */
#define MAXLINE 65507
#define FUNC_RET(func, val) \
do { \
int rv; \
if ( (rv=func) < 0) { \
printf("LINE = %d, error str: %s\n", __LINE__, strerror(errno)); \
perror("FUNC_RET"); \
exit(-1); \
} \
val = rv; \
} while (0);
int
main(int argc, char **argv)
{
int sockfd, n, ret;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr;
if (argc != 2) {
printf("usage: %s ip_addr\n", argv[0]);
exit(0);
}
/*
* socket函数是创建一个socket套接字,第一个参数是AF_INET表明是IPV4协议, SOCK_STREAM表明是传送的是字节流。
* 第三个参数为零, 则默认AF_INET和SOCK_STREAM组合是TCP协议。
*/
FUNC_RET(socket(AF_INET, SOCK_STREAM, 0), sockfd);
bzero(&servaddr, sizeof(servaddr)); // 将auto变量servaddr清零。
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(13); // 13是时间获取服务器众所周知的端口。
/* 将十进制点分式的IP地址转换成网络格式 */
FUNC_RET(inet_pton(AF_INET, argv[1], &servaddr.sin_addr), ret);
/* connect函数应用于TCP套接字时候, 将sockfd与servaddr结构指定的服务器建立一个TCP连接 */
FUNC_RET(connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)), ret);
/* 从连接的服务器处读取内容 */
while ( (n=read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0;
if (fputs(recvline, stdout) == EOF) {
perror("fputs error:");
exit(0);
}
}
if (n < 0) {
perror("read error: ");
}
exit(0);
}
/*
在ubuntu下编译生成client可执行文件。
等服务端先运行后,执行: ./client 127.0.0.1
结果为:
Sun Aug 7 11:33:42 2016
127.0.0.1这个IP是本地回环IP, client程序连接这个IP表明是和自己本机通信。
*/