>TCP协议位于传输层,是一种面向链接的可靠的传输协议程序员
>socket(套接字):是IP地址与端口号的统称编程
>套接字的基本结构服务器
struct sockaddr 这个结构用来存储套接字地址网络
结构体的定义dom
struct sockaddr {socket
unsigned short sa_family; /* address族, AF_xxx */ide
har sa_data[14]; /* 14 bytes的协议地址 */函数
};工具
sa_family 通常来讲,都是“AFINET”。spa
sa_data 包含了一些远程电脑的地址、端口和套接字的数目,它里面的数据是杂溶在一切的。
为了处理struct sockaddr, 程序员创建了另一个类似的结构 struct sockaddr_in:
struct sockaddr_in (“in” 表明 “Internet”)
struct sockaddr_in {
short int sin_family; /* Internet地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* Internet地址 */
unsigned char sin_zero[8]; /* 添0(和struct sockaddr同样大小)*/
};
这个结构提供了方便的手段来访问socket address(struct sockaddr)结构中的每个元素
>IP 地址转换
Linux 系统提供和不少用于转换IP 地址的函数.首先,假设你有一个struct sockaddr_in ina,而且你的IP 166.111.69.52 ,你想把你的IP 存储到ina 中。你可使用的函数: inet_addr() ,它可以把一个用数字和点表示IP 地址的字符串转换成一个无符号长整型。你能够像下面这样使用它:
ina.sin_addr.s_addr = inet_addr(“166.111.69.52”);
注意:
inet_addr() 返回的地址已是网络字节顺序了,你没有必要再去调用htonl() 函数反过来,若是你有一个struct in_addr 而且你想把它表明的IP 地址打印出来(按照数字.数字.数字.数字的格式),那么你可使用函数inet_ntoa()(“ntoa”表明“Network to ASCII”),它会把struct in_addr 里面存储的网络地址以数字.数字.数字.数字的格式。
l inet_ntoa() 使用struct in_addr 做为一个参数,不是一个长整型值。
>套接字字节转换程序的列表:
l htons()——“Host to Network Short”主机字节顺序转换为网络字节顺序(对无符号短型进行操做4 bytes)
l htonl()——“Host to Network Long” 主机字节顺序转换为网络字节顺序(对无符号长型进行操做8 bytes)
l ntohs()——“Network to Host Short “ 网络字节顺序转换为主机字节顺序(对无符号短型进行操做4 bytes)
l ntohl()——“Network to Host Long “ 网络字节顺序转换为主机字节顺序(对无符号长型进行操做8 bytes)
>基本套接字调用
socket() 函数
取得套接字描述符
socket 函数的定义是下面这样子的:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain , int type , int protocol);
bind() 函数
bind()函数能够帮助你指定一个套接字使用的端口。
当你使用socket() 函数获得一个套接字描述符,你也许须要将socket 绑定上一个你的机器上的端口。当你须要进行口监听 listen()操做,等待接受一个连入请求的时候,通常都须要通过这一步。好比网络泥巴(MUD),Telnet a.b.c.d 4000.若是你只是想进行链接一台服务器,也就是进行 connect() 操做的时候,这一步并非必须的。
bind()的系统调用声明以下:
#include <sys/types.h>
#include <sys/socket.h>
int bind (int sockfd , struct sockaddr *my_addr , int addrlen) ;
参数说明:
l sockfd 是由socket()函数返回的套接字描述符。
l my_addr 是一个指向struct sockaddr 的指针,包含有关你的地址的信息:名称、端口和IP 地址。
addrlen 能够设置为sizeof(struct sockaddr)。
connect()函数
让咱们花一点时间来假设你是一个Telnet 应用程序。你的使用者命令你创建一个套接字描述符。你听从命令,调用了socket()。而后,使用者告诉你链接到“166.111.69.52”的23 端口(标准的Telnet 端口)你应该怎么作呢?
你很幸运:Telnet 应用程序,你如今正在阅读的就是套接字的进行网络链接部分:
connect()。
connect() 函数的定义是这样的:
#include <sys/types.h>
#include <sys/socket.h>
int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);
connect()的三个参数意义以下:
l sockfd :套接字文件描述符,由socket()函数返回的。
l serv_addr 是一个存储远程计算机的IP 地址和端口信息的结构。
l addrlen 应该是sizeof(struct sockaddr)。
listen() 函数
listen()函数是等待别人链接,进行系统侦听请求的函数。当有人链接你的时候,你有两步须要作:经过listen()函数等待链接请求,而后使用accept()函数来处理。(accept()函数在下面介绍)。
listen()函数调用是很是简单的。函数声明以下:
#include <sys/socket.h>
int listen(int sockfd, int backlog);
listen()函数的参数意义以下:
l sockfd 是一个套接字描述符,由socket()系统调用得到。
l backlog 是未通过处理的链接请求队列能够容纳的最大数目。
backlog 具体一些是什么意思呢?每个连入请求都要进入一个连入请求队列,等待listen 的程序调accept((accept()函数下面有介绍)函数来接受这个链接。当系统尚未调用accept()函数的时候,若是有不少链接,那么本地可以等待的最大数目就是backlog 的数值。你能够将其设成5 到10 之间的数值
accept()函数
函数accept()有一些难懂。当调用它的时候,大体过程是下面这样的:
有人从很远很远的地方尝试调用 connect()来链接你的机器上的某个端口(固然是你已经在listen()的)。他的链接将被 listen 加入等待队列等待accept()函数的调用(加入等待队列的最多数目由调用listen()函数的第二个参数backlog 来决定)。你调用 accept()函数,告诉他你准备链接。accept()函数将回返回一个新的套接字描述符,这个描述符就表明了这个链接.好,这时候你有了两个套接字描述符,返回给你的那个就是和远程计算机的链接,而第一个套接字描述符仍然在你的机器上原来的那个端口上listen()。这时候你所获得的那个新的套接字描述符就能够进行send()操做和recv()操做了。
下面是accept()函数的声明:
#include <sys/socket.h>
int accept(int sockfd, void *addr, int *addrlen);
accept()函数的参数意义以下:
l sockfd 是正在listen() 的一个套接字描述符。
l addr 通常是一个指向struct sockaddr_in 结构的指针;里面存储着远程链接过来的计算机的信息(好比远程计算机的IP 地址和端口)
> 下面咱们就来模拟一个socket编程的TCP协议
server.c 的做用是接受client的请求,并与client进行简单的数据通讯,总体为一个阻塞式的网络聊天工具
//server.c
1#include<stdio.h>
2 #include<stdlib.h>
3 #include<sys/types.h>
4 #include<sys/socket.h>
5 #include<netinet/in.h>
6 #include<string.h>
7 #include<error.h>
8 #include<arpa/inet.h>
9 #include<pthread.h>
10 static void usage(const char* proc)
11 {
12 printf("Usage: %s [ip] [port]\n",proc);
13 }
14 void *thread_run(void* arg)
15 {
16 printf("creat a new thread\n");
17 int fd=(int)arg;
18 char buf[1024];
19 while(1)
20 {
21 memset(buf,'\0',sizeof(buf));
22 ssize_t _s=read(fd,buf,sizeof(buf)-1);
23 if(_s>0)
24 {
25 printf("client#: %s\n",buf);
26 write(fd,buf,strlen(buf));
27 }
28 else if(_s==0)
29 {
30 printf("client close...\n");
31 break;
32 }
33 else
34 {
35 printf("read eror...\n");
36 break;
37 }
38 return (void*)0;
39 }
40 }
41 int main(int argc,char* argv[])
42 {
43 if(argc!=3)
44 {
45 usage(argv[0]);
46 exit(1);
47 }
48 int listen_sock=socket(AF_INET,SOCK_STREAM,0);
49 if(listen_sock<0)
50 {
51 perror("socket");
52 exit(2);
53 }
54 struct sockaddr_in server_socket;
55 server_socket.sin_family=AF_INET;
56 server_socket.sin_addr.s_addr=inet_addr(argv[1]);
57 server_socket.sin_port=htons(atoi(argv[2]));
58 int opt=1;
59 setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
60
61 if(bind(listen_sock,(struct sockaddr*)&server_socket,sizeof(server_socke t))<0)
62 {
63 perror("bind");
64 exit(3);
65 }
66 listen(listen_sock,5);
67 struct sockaddr_in peer;
68 socklen_t len=sizeof(peer);
69 int fd=accept(listen_sock,(struct sockaddr*)&peer,&len);
70 printf("fd: %d\n",fd);
71 if(fd<0)
72 {
73 perror("accept");
74 exit(4);
75 }
76 printf("get a new link,socket->[%s] [%d]",inet_ntoa(peer.sin_addr),ntohs (peer.sin_port));
77 while(1)
78 {
79 pthread_t id;
80 pthread_create(&id,NULL,thread_run,(void*)fd);
81 pthread_join(id,NULL);
82 }
83 return 0;
84 }
//client.c
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<sys/socket.h>
4 #include<netinet/in.h>
5 #include<arpa/inet.h>
6 #include<string.h>
7 #include<pthread.h>
8 #include<error.h>
9 #include<stdlib.h>
10 static void usage(const char *proc)
11 {
12 printf("usage: %s [ip] [port]\n",proc);
13 }
14 int main(int argc,char* argv[])
15 {
16 if(argc!=3)
17 {
18 usage(argv[0]);
19 exit(1);
20 }
21 int sock=socket(AF_INET,SOCK_STREAM,0);
22 if(sock==0)
23 {
24 perror("socket");
25 exit(2);
26 }
27 struct sockaddr_in client_sock;
28 client_sock.sin_family=AF_INET;
29 client_sock.sin_addr.s_addr=inet_addr(argv[1]);
30 client_sock.sin_port=htons(atoi(argv[2]));
31
32 if(connect(sock,(struct sockaddr*)&client_sock,sizeof(client_sock))<0)
33 {
34 perror("connect");
35 exit(3);
36 }
37 char buf[1024];
38 while(1)
39 {
40 memset(buf,'\0',sizeof(buf));
41 printf("Please Enter:");
42 fflush(stdout);
43 ssize_t _s=read(0,buf,sizeof(buf)-1);
44 if(_s>0)
45 {
46 buf[_s-1]='\0';
47 write(sock,buf,strlen(buf));
48 _s=read(sock,buf,sizeof(buf));
49 if(_s>0)
50 {
51 printf("%s\n",buf);
52 }
53 }
54 }
55 return 0;
56 }
运行结果: