socket(套接字):IP地址+端口号,唯一标识网络中的一个进程
socket编程中建立连接的两个进程都有一个socket来标识唯一一个连接。
网络字节序:网络数据流有大小端之分,发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存,网络数据流的地址规定:先发出的数据是低地址,后发出的数据是
地址。
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
例:如果端口号是1000(0x3e8),则地址0是0x03,地址1是0xe8, 也就是先发0x03,再发0xe8,这16位在发送主机的缓冲区中也应该是低地址存0x03,高地址存0xe8。但是,如果发送主机是小端字节序的,这16位端口号被解释成0xe803,不是1000。因此,发送主机把1000填到发送缓冲区之前需要做字节序的转换。同样地,接收主机如果是小端字节序的, 接到16位的源端口号也要做字节序的转换。如果主机是大端字节序的,发送和接收都不需要做转换。同理,32位的IP地址也要考虑网络字节序和主机字节序的问题。
服务器端socket:
1.创建套接字:int socket(int domain,int type,int protocol) //domain底层采用何种协议,传输类型,TCP面向字节流式
2.使用struct socketaddr_in 结构体填充网络方面信息
3.绑定:将套接字信息填充到内核中
4.设置套接字状态为监听状态,接收客户端发来的连接请求
5.等待客户请求,当请求连接来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)
6.新的套接字和客户端进行通信
accept函数:在一个套接口接受一个连接。
int accept(int sockfd,struct sockaddr*addr,socklen_t* addrlen)
sockfd和 listen() 中套接字描述符相同。addr要求接入的信息所要去的地方。返回值:如果执行成功,返回值为0,否则设置错误码,返回-1。
bind函数:将一本地地址与一套接口捆绑。
int bind(int sockfd, const struct sockaddr* addr, socklen_t len);
适用于未连接的数据报或流类套接口,在connect()或listen()调用前使用。当用socket()创建套接口后,它便存在于一个名字空间(地址族)中,但并未赋名。bind()函数通过给一个未命名套接口分配一个本地名字来为套接口建立本地捆绑(主机地址/端口号)。
服务器端代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #include<sys/types.h> 5 #include<sys/socket.h> 6 #include<netinet/in.h> 7 #include<arpa/inet.h> 8 #include<pthread.h> 9 int start(int _port,char* ip) 10 { 11 //1.create sock 12 int listen_sock=socket(AF_INET,SOCK_STREAM,0); 13 if(listen_sock < 0){ 14 perror("sock"); 15 exit(1); 16 } 17 //2.fill information 18 struct sockaddr_in local; 19 local.sin_family=AF_INET; 20 local.sin_port=htons(_port); 21 local.sin_addr.s_addr=inet_addr(ip); 22 //3.bind 23 if(bind(listen_sock,(struct sockaddr*)&local,sizeof(local))<0) 24 { 25 perror("bind"); 26 exit(2); 27 } 28 //4.set listen 29 if(listen(listen_sock,5)<0) 30 { 31 perror("listen"); 32 exit(3); 33 } 34 return listen_sock; 35 36 37 } 38 void usage(const char *proc) 39 { 40 printf("usage:%s [ip] [port]\n",proc); 41 } 42 43 void* thread_run(void* arg) 44 { 45 int sock =(int)arg; 46 char buf[1024]; 47 while(1) 48 { 49 memset(buf,‘\0‘,sizeof(buf)); 50 ssize_t _size=read(sock,buf,sizeof(buf)-1); 51 if(_size<0) 52 { 53 printf("read fail...\n"); 54 exit(1); 55 }else if(_size==0) 56 { 57 printf("closed connect...\n"); 58 exit(2); 59 }else 60 { 61 printf("client# %s\n",buf); 62 } 63 } 64 } 65 66 int main(int argc,char* argv[]) 67 { 68 if(argc!=3) 69 { 70 usage(argv[0]); 71 exit(1); 72 } 73 int listen_sock=start(atoi(argv[2]),argv[1]); 74 struct sockaddr_in client; 75 socklen_t len=sizeof(client); 76 int done=0; 77 while(!done) 78 { 79 int new_sock=accept(listen_sock,(struct sockaddr*)&client,&len); 80 if(new_sock<0) 81 { 82 perror("accept"); 83 continue; 84 } 85 printf("get a connect... [sock]:%d [ip]:%s [port]:%d\n",new_sock,inet_ntoa(cl ient.sin_addr),ntohs(client.sin_port)); 86 87 #ifdef _V1_ 88 while(1) 89 { 90 char buf[1024]; 91 memset(buf,‘\0‘,sizeof(buf)); 92 ssize_t _size=read(new_sock,buf,sizeof(buf)-1); 93 if(_size<0) 94 { 95 printf("read fail...\n"); 96 exit(1); 97 }else if(_size==0) 98 { 99 printf("closed connect...\n"); 100 exit(2); 101 }else 102 { 103 printf("client# %s",buf); 104 } 105 } 106 } 107 #elif _V2_ 108 pid_t id=fork(); 109 if(id<0) 110 { 111 printf("create fail...\n"); 112 exit(3); 113 }else if(id==0) 114 { 115 close(listen_sock);//close listen_sock,only deal with I/Ostream 116 while(1) 117 { 118 char buf[1024]; 119 memset(buf,‘\0‘,sizeof(buf)); 120 ssize_t _size=read(new_sock,buf,sizeof(buf)-1); 121 if(_size<0)
121 if(_size<0)
122 {
123 printf("read fail...\n");
124 exit(1);
125 }else if(_size==0)
126 {
127 printf("closed connect...\n");
128 exit(2);
129 }else
130 {
131 printf("client# %s\n",buf);
132 }
133 }
134 close(new_sock);
135 }else
136 {
137 close(new_sock);
138 }
139 }
140
141 #elif _V3_
142 pthread_t tid;
143 if(tid=pthread_create(&tid,NULL,thread_run,(void*) new_sock)<0)
144 {
145 perror("pthread_create");
146 exit(2);
147 }
148 pthread_detach(tid);
149 }
150 #else
151 printf("default\n");
152 }
153 #endif
154 return 0;
155 }
connect函数:
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
向服务器端发送连接请求,只维护一个连接,连接成功后通过sock进行传输
客户端:
1.创建套接字(进行三次握手)
2.向服务器发连接请求
3.传输数据
4.关闭套接字(四次挥手,即释放链接)
客户端代码:
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/socket.h>
6 #include<netinet/in.h>
7 #include<arpa/inet.h>
8
9 void usage(char* proc)
10 {
11 printf("usage:%s [remoteip] [remoteport]\n",proc);
12 }
13 int main(int argc,char*argv[])
14 {
15 if(argc!=3)
16 {
17 usage(argv[0]);
18 exit(1);
19 }
20 char* remote_ip=argv[1];
21 int remote_port=atoi(argv[2]);
22 int sock=socket(AF_INET,SOCK_STREAM,0);
23 if(sock < 0)
24 {
25 perror("socket");
26 exit(2);
27 }
28 struct sockaddr_in remote;
29 remote.sin_family=AF_INET;
30 remote.sin_port=htons(remote_port);
31 remote.sin_addr.s_addr=inet_addr(remote_ip);
32 //send SYN head
33 if(connect(sock,(struct sockaddr*)&remote,sizeof(remote))<0)
34 {
35 perror("connect");
36 exit(3);
37 }
38 char msg[1024];
39 while(1)
40 {
41 memset(msg,‘\0‘,sizeof(msg));
42 printf("please input msg:");
43 fflush(stdout);
44 if(read(0,msg,sizeof(msg))>0){
45 write(sock,msg,strlen(msg));
46 }
47
48 }
49 return 0;
50 }
运行结果:
单用户:
利用线程模拟多用户: