1、网络字节序和主机字节序
网络字节序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节序采用big endian排序方式。
不同的CPU有不同的字节序类型,这些字节序是指 整数 在内存中保存的顺序,这个叫做主机字节序,有大端小端两种。
htons()--"Host to Network Short"
htonl()--"Host to Network Long"
ntohs()--"Network to Host Short"
ntohl()--"Network to Host Long"
2、几个结构体
代码语言:javascript复制struct sockaddr {
unsigned short sa_family; /* 地址家族, AF_xxx */
char sa_data[14]; /*14字节协议地址*/
};
代码语言:javascript复制struct sockaddr_in {
short int sin_family; /* 通信类型 */
unsigned short int sin_port; /* 端口 */
struct in_addr sin_addr; /* Internet 地址 */
unsigned char sin_zero[8]; /* 与sockaddr结构的长度相同*/
};
代码语言:javascript复制struct in_addr {
unsigned long s_addr;
};
3、inet_addr和inet_ntoa
函数inet_addr(),将IP地址从字符串转换成无符号长整型,注意,inet_addr()返回的地址已经是网络字节格式
代码语言:javascript复制ina.sin_addr.s_addr = inet_addr("132.241.5.10");
inet_ntoa()将结构体in-addr作为一个参数,不是长整形。同样需要注意的是它返回的是一个指向一个字符的指针
代码语言:javascript复制printf("%s",inet_ntoa(ina.sin_addr));
4、socket()函数
代码语言:javascript复制int socket(int domain, int type, int protocol);
domain:即协议域,又称为协议族(family)。常用的协议族有AF_INET
type:指定socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等等(socket的类型有哪些?)。
protocol:故名思意,就是指定协议。当protocol为0时,会自动选择type类型对应的默认协议。
5、bind()函数
绑定套接字到指定的IP地址和端口号
代码语言:javascript复制int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
6、connect()
serv_addr 是保存着目的地端口和IP地址的数据结构 struct sockaddr
至于客户端,内核将自动选择一个合适的端口号
代码语言:javascript复制int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
7、listen()
bind()之后用listen,监听绑定的端口号
代码语言:javascript复制int listen(int sockfd, int backlog);
使用两个队列实现,一个SYN队列(或半连接队列)和一个accept队列(或完整的连接队列)。 处于SYN RECEIVED状态的连接被添加到SYN队列,并且当它们的状态改变为ESTABLISHED时,即当接收到3次握手中的ACK分组时,将它们移动到accept队列。 显而易见,accept系统调用只是简单地从完成队列中取出连接。 在这种情况下,listen syscall的backlog参数表示完成队列的大小
8、accept()函数
accept()函数实际做的是在已完成连接队列列头返回下一个已完成连接,服务器三路握手在listen()函数之后,accept()之前, 由内核来自动完成了三路握手。
函数通过后两个参数返回客户端的sockaddr_in结构体和长度
返回值是一个新的套接字文件描述符,这样就有两个套接字了,原来的一个还在侦听你的那个端口, 新的在准备发送 (send()) 和接收 ( recv()) 数据
代码语言:javascript复制int accept(int sockfd, void *addr, int *addrlen);
9、send()和recv()函数
这两个函数用于流式套接字或者数据报套接字的通讯。flag一般为0。
代码语言:javascript复制int send(int sockfd, const void *msg, int len, int flags);
代码语言:javascript复制int recv(int sockfd, void *buf, int len, unsigned int flags);
如果你用 connect() 连接一个数据报套接字,你可以简单的调用 send() 和 recv() 来满足你的要求
10、sendto() 和 recvfrom()函数
数据报套接字使用
代码语言:javascript复制int sendto(int sockfd, const void *msg, int len, unsigned int flags,
const struct sockaddr *to, int tolen);
代码语言:javascript复制int recvfrom(int sockfd, void *buf, int len, unsigned int flags,
struct sockaddr *from, int *fromlen);
11、getpeername()函数
函数 getpeername() 告诉你在连接的流式套接字上谁在另外一边
代码语言:javascript复制int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
12、gethostname()函数
返回你程序所运行的机器的主机名字
代码语言:javascript复制int gethostname(char *hostname, size_t size);
13、gethostbyname()函数
多用于客户端。DNS域名服务
代码语言:javascript复制struct hostent *gethostbyname(const char *name);
代码语言:javascript复制struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};
14、TCP的例子
服务器:
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define MYPORT 3490 /*定义用户连接端口*/
#define BACKLOG 10 /*多少等待连接控制*/
main()
{
int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */
struct sockaddr_in my_addr; /* my address information */
struct sockaddr_in their_addr; /* connector's address information */
int sin_size;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
my_addr.sin_family = AF_INET; /* host byte order */
my_addr.sin_port = htons(MYPORT); /* short, network byte order */
my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */
bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))== -1) {
perror("bind");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
while(1) { /* main accept() loop */
sin_size = sizeof(struct sockaddr_in);
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
perror("accept");
continue;
}
printf("server: got connection from %sn",
inet_ntoa(their_addr.sin_addr));
if (!fork()) { /* this is the child process */
if (send(new_fd, "Hello, world!n", 14, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd); /* parent doesn't need this */
while(waitpid(-1,NULL,WNOHANG) > 0); /* clean up child processes */
}
}
客户端:
代码语言:javascript复制#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define PORT 3490 /* 客户机连接远程主机的端口 */
#define MAXDATASIZE 100 /* 每次可以接收的最大字节 */
int main(int argc, char *argv[])
{
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in their_addr; /* connector's address information */
if (argc != 2) {
fprintf(stderr,"usage: client hostnamen");
exit(1);
}
if ((he=gethostbyname(argv[1])) == NULL) { /* get the host info */
herror("gethostbyname");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
their_addr.sin_family = AF_INET; /* host byte order */
their_addr.sin_port = htons(PORT); /* short, network byte order */
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(their_addr.sin_zero),; /* zero the rest of the struct */
if(connect(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1) {
perror("connect");
exit(1);
}
if ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
perror("recv");
exit(1);
}
buf[numbytes] = '