bind:
bind函数把一个本地协议地址赋予一个套接字。对于网际网协议,协议地址是32位的IPv4地址或128位的IPv6地址与16位的TCP或UDP端口号的组合。#include
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函数参数:
返回值:成功 0 出错 -1
bind函数指定要捆绑的IP地址和/或端口号:
进程指定 |
结果 |
|
IP地址 |
端口 |
|
通配地址 |
0 |
内核选择IP地址和端口 |
通配地址 |
非0 |
内核选择IP地址,进程指定端口 |
本地IP地址 |
0 |
进程指定IP地址,内核选择端口 |
本地IP地址 |
非0 |
进程指定IP地址和端口 |
struct sockaddr_in addr;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
IPv6:IPv6地址存放在结构中,系统预先分配变量in6addr_any的extern声明并将其初始化为常值IN6ADDR_ANY_INIT(extern const struct in6_addr in6addr_any; /*::*/)
struct sockaddr_in6 addr;
addr.sin_addr = in6addr_any;
typedef struct sockaddr SA;
//定义一个struct sockaddr_in类型的变量并清空
listenfd = socket(PF_INET, SOCK_STREAM, 0);
struct sockaddr_in myaddr;
bzero(&myaddr, sizeof(myaddr));
//填充地址信息
my_addr.sin_family = PF_INET;
my_addr.sinport = htons(8888);
my_addr.sin_addr.s_addr = inet_addr(“192.168.1.100”);
//将my_addr强制转换为struct sockaddr类型在函数中使用
int status = bind(listenfd, (SA *)&my_addr, sizeof(my_addr));
connect:
TCP客户用connect函数来建立与TCP服务器的连接
#include
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
函数参数:
返回值:成功 0 出错 -1
typedef struct sockaddr SA;
int status = connect(sockfd, (SA *)&addr, sizeof(addr));
执行TCP客户端的通常步骤:创建一个TCP套接字并连接到一个服务器
int tcp_connect(const char *hostname, const char *service);
返回值:成功则返回已连接套接字描述符,出错不返回
int tcp_connect(const char *host, const char *serv)
{
int sockfd, n;
struct addrinfo hints, *res, *ressave;
bzero(&hints, sizeof (struct addrinfo)) ;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((n = getaddrinfo (host, serv, &hints, &res)) != 0)
err_quit("tcp_connect error for %s, %s: %s",host, serv, gai_strerror(n)) ;
ressave = res;
do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0)
continue; /* ignore this one */
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
break; /* success */
close (listenfd); /* ignore this one */
} while ( (res = res->ai_next) != NULL);
if (res == NULL) /* errno set from final connect() */
err_sys ("tcp_connect error for %s, %s", host, serv);
freeaddrinfo (ressave);
return (sockfd);
}
#include
int listen(int sockfd, int backlog);
函数参数:
listen(listenfd,5);
int tcp_listen(const char *hostname, const char *service, socklen_t *addrlenp);
返回值:成功则返回已连接套接字描述符,出错不返回
int tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
{
int listenfd, n;
const int on = 1;
struct addrinfo hints, *res, *ressave;
bzero(&hints, sizeof (struct addrinfo)) ;
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((n = getaddrinfo (host, serv, &hints, &res)) != 0)
err_quit("tcp_listen error for %s, %s: %s",host, serv, gai_strerror(n)) ;
ressave = res;
do {
listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (listenfd < 0)
continue; /* error, try next one */
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on) ) ;
if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
break; /* success */
close (listenfd); /* bind error, close and try next one */
} while ( (res = res->ai_next) != NULL);
if (res == NULL) /* errno from final socket () or bind () */
err_sys ("tcp_listen error for %s, %s", host, serv);
listen (listenfd, LISTENQ);
if (addrlenp)
*addrlenp = res->ai_addrlen; /* return size of protocol address */
freeaddrinfo (ressave);
return (listenfd);
}
#include
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
函数参数:
accept函数最多返回一下三个值中的其中之一:
如果对返回客户协议地址无兴趣,将cliaddr,addrlen均置为空指针。
typedef struct sockaddr SA;
struct sockaddr_in cliaddr;
socklen_t clilen = sizeof(cliaddr);
int connfd = accept(listenfd,(SA *)&cliaddr, &clilen);
ssize_t recv(int socket, const void *buffer, size_t length, int flags);
函数参数:
int status = recv(sockfd, buf, sizeof(buf), 0);
ssize_t send(int socket, const void *buffer, size_t length, int flags);
函数参数:
int status = send(sockfd, buf, sizeof(buf), 0);
read/write:
ssize_t read(int fd, const void *buf, size_t count);
读一个字节流套接字,从一个描述符读n字节
ssize_t readn(int fd, const void *buf, size_t nbytes);
ssize_t readn(int fd, void *vptr, size_t n) /* Read "n" bytes from a descriptor. */
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0)
{
if ((nread = read(fd, ptr, nleft)) < 0)
{
if (errno == EINTR)
nread = 0; /* and call read() again */
else
return(-1);
} else if (nread == 0)
break; /* EOF */
nleft -= nread;
ptr += nread;
}
return(n - nleft); /* return >= 0 */
}
ssize_t readline(int fd, void *buf, size_t maxlen);
static int read_cnt;
static char *read_ptr;
static char read_buf[MAXLINE];
static ssize_t my_read(int fd, char *ptr) //每次最多读取MAXLINE个字符,调用一次,每次只返回一个字符
{
if (read_cnt <= 0) {
again:
if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) //如果读取成功,返回read_cnt=读取的字符
{
if (errno == EINTR)
goto again;
return(-1);
} else if (read_cnt == 0)
return(0);
read_ptr = read_buf;
}
read_cnt--; //每次递减1,直到<0读完,才执行上面if的命令。
*ptr = *read_ptr++; //每次读取一个字符,转移一个字符
return(1);
}
ssize_t readline(int fd, void *vptr, size_t maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for (n = 1; n < maxlen; n++) {
if((rc = my_read(fd, &c)) == 1) {
*ptr++ = c;
if (c == '\n')
break; /* newline is stored, like fgets() */
} else if (rc == 0) {
*ptr = 0;
return(n - 1); /* EOF, n - 1 bytes were read */
} else
return(-1); /* error, errno set by read() */
}
*ptr = 0; /* null terminate like fgets() */
return(n);
}
// readlinebuf函数能够展露内部缓冲区的状态,便于调用者查看在当前文本行之后是否收到了新的数据
ssize_t readlinebuf(void **vptrptr)
{
if (read_cnt)
*vptrptr = read_ptr;
return(read_cnt);
}
ssize_t write(int fd, const void *buf, size_t count);
写一个字节流套接字,往一个描述符写n字节
ssize_t written(int fd, const void *buf, size_t nbytes);
ssize_t writen(int fd, const void *vptr, size_t n) /* Write "n" bytes to a descriptor. */
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0; /* and call write() again */
else
return(-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return(n);
}
close/shutdown:
close函数用来关闭套接字,并终止TCP连接
int close(int socketfd);
int shutdown(int sockfd, int howto);
使用shutdown可以不管引用计数就激发TCP的正常连接终止序列。针对不同的howto,系统会采取不同的关闭方式