#include
int main(int argc,char **argv)
{
int sockfd,n;
char recvline[MAXLINE+1];
struct sockaddr_in servaddr;
if(argc !=2)
err_quit("usage: a.out " );
if((sockfd=socket(AF_INET,SOCK_STREAM,0)) < O)
err_sys("socket error");
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(13); / * daytime server */
if(inet_pton(AF_INET,argv[1],&servaddr.sin.addr)<=0)
err_quit("inet_pton error for % s",argv[1]);
if( connect(sockfd, (SA * ) &servaddr ,sizeof(servaddr)) < 0)
err_sys( "connect error");
while( (n = read(sockfd, recvline,MAXLINE )) >0){
recvline[n]=0; /* null terminate */
if(fputs(recvline, stdout)==EOF)
err_sys("fputs error");
}
if(n<0)
err_sys(" read error");
exit(0);
}
socket创建一个网际(AF_INET)字节流(SOCK_STREAM)套接字
由于不同的机器主机字节序不同,不同体系结构的机器之间无法通信,所以要转换成一种约定的字节序,也就是网络字节序。即使是同一台机器上的两个进程(比如一个由C语言,另一个由Java编写)通信,也要考虑字节序的问题(JVM采用大端字节序)。
网络字节序与主机字节序之间的转换函数: htons()…ntohs)…htonl()、ntohl).htons和ntohs完成16位无符号数的相互转换,htonl和ntohl完成32位无符号数的相互转换。host to network short long
这两个函数是随IPv6出现的函数,对于IPv4地址和IPv6地址都适用,函数中p和n分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构的二进制值。
#include
int inet_pton(int family, const char *strptr, void *addrptr); //将点分十进制的ip地址转化为用于网络传输的数值格式
返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len); //将数值格式转化为点分十进制的ip地址格式
返回值:若成功则为指向结构的指针,若出错则为NULL
int connect(int sockfd, const struct sockaddr *servaddr, int *addrlen);
返回:若成功则返回0,失败则返回-1;
sockfd是有socket函数返回的套接字描述符,第二个、第三个参数分别是一个指向套接字地址结构的指针和该结构的大小。套接字地址结构必须含有服务器的IP地址和端口号。
我们使用read函数读取服务器的应答,并用标准的I/O函数fputs输出结果。
当发生错误时,调用自己的err_quit或err_sys函数输出一个错误信息并终止程序的运行,所以定义包裹函数来缩短程序。约定包裹函数名是实际函数名的首字母大写形式。例如:
socket()包裹函数Socket()
int Socket(int family, int type, int protocol)
{
int n = socket(family, type, protocol);
if(n < 0)
{
perror("socket error!\n");
exit(0);
}
return n;
}
#include "unp.h"
#include
int
main(int argc, char **argv)
{
int listenfd, connfd;
struct sockaddr_in *servaddr;
char buff[MAXLINE];
time_t ticks;
listenfd = Soceket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(13);
Bind(listenfd, (SA *)&servaddr, sizeof(servaddr));
Listen(listenfd, LISTENO);
for ( ; ; ) {
connfd = Accept(listenfd, (SA *)NULL, NULL);
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Write(connfd, buff, strlen(buff));
Close(connfd);
}
}
1.把服务器的众所周知端口捆绑到套接字。我们制定IP地址为INADDR_ANY,这样要是服务器主机有多个网络接口,服务器进程就可以在任意网络接口上接受客户端连接。
2.将套接字转换成监听套接字。调用listen函数。
3.接受客户端连接,发送应答。TCP连接使用所谓的三路握手(three-way handshake)来建立连接。握手完毕时accept返回,其返回值是一个称为已连接描述符的新描述符。
4.终止连接close