网络编程之套接字(tcp)

  经过几天高强度的学习,对套接字的编程有了初步的认识,今天对这几天所学的知识总结一下;首先简单阐述一下tcp通信;

  TCP提供的是可靠的,顺序的,以及不会重复的数据传输,处理流控制,由于TCP是可靠的,连接的,顺序的,所以TCP一般用于都应用于对传输的完整性,正确性要求严的场合;编写基于tcp的服务器-客户端模型的程序简单流程如下:

  服务器端:

  (1)调用socket()创建一个套接口

  (2)调用bind()函数是服务器进程与一个端口绑定

  (3)调用listen()设置客户接入队列的大小

  (4)调用accept()接受一个连接,如果介入的队列不为空,则返回一个已连接的套接口描述符,

  (5)调用sned()和recv()函数用来在已连接的套接口间进行发送和接收数据

  客户端:

  (1)调用socket()创建套接字

  (2)调用connect()函数向服务器发送连接请求;

  (3)调用send()函数和recv()函数

  下面是服务器端的代码;

 1 #include <stdio.h>
 2 #include <sys/socket.h>
 3 #include <netinet/in.h>
 4 
 5 //server
 6 int main()
 7 {
 8     int fd = 0;
 9     int nfd = 0;
10     int ret = 0;
11     unsigned char data[1024] = {0};
12 
13     fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
14     if(fd < 0) {
15         perror("socket");
16         return 1;
17     }
18     /*服务端信息*/
19     struct sockaddr_in ser;
20     ser.sin_family = AF_INET;
21     ser.sin_port = htons(2009);
22     ser.sin_addr.s_addr = htonl(0xc0a8010a);//192.168.1.10
23     struct sockaddr_in clt;
24     int len = 0;
25 
26     /*绑定*/
27     ret = bind(fd, (struct sockaddr *)&ser, 16);
28     if(ret < 0) {
29         perror("bind");
30         return 1;
31     }
32 
33     /*监听*/
34     ret = listen(fd, 10);
35     if(ret == -1) {
36         perror("listen");
37         return 1;
38     }
39 
40     /*接收连接,并且返回一个新的套接字描述符nfd*/
41     nfd = accept(fd, (struct sockaddr *)&clt, &len);
42     if(ret < 0) {
43         perror("accept");
44         return 1;
45     }
46     
47     /*接收*/    
48     ret = recv(nfd, data, 1024, 0);
49     if(ret < 0) {
50         perror("recv");
51         return 1;
52     }
53     printf("clt said: %s\n", data);
54     close(fd);
55     
56     return 0;
57 }
服务器端

 

 1 #include <stdio.h>
 2 #include <sys/socket.h>
 3 #include <netinet/in.h>
 4 #include <string.h>
 5 
 6 //client
 7 int main()
 8 {
 9     int sock_fd = 0;
10     int ret = 0;
11     unsigned char *buf = "hello, hao ara you";
12 
13     /*创建一个套接口*/    
14     sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
15     if(sock_fd < 0) {
16         perror("socket");
17         return 1;
18     }
19     
20     /*服务端信息*/
21     struct sockaddr_in ser;
22     ser.sin_family = AF_INET;
23     ser.sin_port = htons(2009);
24     ser.sin_addr.s_addr = inet_addr("192.168.1.10");
25 
26     //建立链接
27     ret = connect(sock_fd, (struct sockaddr *)&ser, 16);
28     if(ret < 0) {
29         perror("connect");
30         return 1;
31     }
32 
33     /*发送*/
34     ret = send(sock_fd, buf, strlen(buf), 0);
35     if(ret < 0) {
36         perror("send");
37         return 1;
38     }
39 
40     close(sock_fd);    
41     return 0;        
42 }
客户端

 

上面程序是基于tcp的简单通信,下面我们利用tcp实现一个服务器多个客户机;要实现一对多,就要使用线程编程,服务器端在不断监听中,如果有连接请求的话,就用通过 accept函数接受并创建一个线程来处理。线程的创建函数为int pthread_create(pthread_t * thread, pthread_attr_t * attr, void * (*start_routine)(void *), void * arg);

  下面是这个程序的源码

 1 #include <stdio.h>
 2 #include <sys/socket.h>
 3 #include <netinet/in.h>
 4 #include <string.h>
 5 #include <pthread.h>
 6 
 7 #define PORT 9527
 8 
 9 void *function(void *d);//线程要执行的函数
10 
11 int main()
12 {
13     /*创建套接口*/
14     pthread_t pid= 0;
15     int nfd = 0; 
16     int fd = 0;
17     fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
18     if(fd < 0) {
19         perror("sock");
20         return 1;
21     }
22     /*服务器信息*/
23     struct sockaddr_in ser;
24     ser.sin_family = AF_INET;
25     ser.sin_port = htons(PORT);
26     ser.sin_addr.s_addr = inet_addr("192.168.1.10");
27     struct sockaddr_in clt;
28     int len = 0;
29 
30     /*绑定*/
31     int ret = bind(fd, (struct sockaddr *)&ser, 16);
32     if(ret == -1) {
33         perror("bind");
34         return 1;
35     }
36 
37     /*监听*/
38     ret = listen(fd, 10);
39     if(ret < 0) {
40         perror("listen");
41         return 1;
42     }
43 
44     while(1) {
45         /*接受链接*/
46         nfd = accept(fd, (struct sockaddr *)&clt, &len);
47         if(nfd < 0) {
48             perror("accept");
49             return 1;
50         }
51 
52         /*创建一个线程*/        
53         ret = pthread_create(&pid, NULL, function, (void *)nfd);    
54         if(ret != 0) {
55             perror("pthread_create");
56             return 1;
57         }
58 
59         pthread_join(pid, NULL);
60 
61         close(nfd);
62     }
63      
64     close(fd);
65     return 0;    
66 }
67 
68 void *function(void *d)
69 {    
70     unsigned char buf[1024] = {0};
71     int nfd = (int )d;
72     int ret = 0;
73         
74     memset(buf, 0, 1024);
75     ret = recv(nfd, buf, 1024, 0);
76     if(ret < 0) {
77         perror("recv");
78         return NULL;
79     }
80     printf("client said: %s\n", buf);
81 
82     ret = send(nfd, "recv ok", 7, 0);
83     if(ret < 0) {
84         perror("send");
85         return NULL;
86     }
87 
88     
89 }
server
 1 #include <stdio.h>
 2 #include <sys/socket.h>
 3 #include <netinet/in.h>
 4 #include <string.h>
 5 
 6 #define PORT 9527
 7 
 8 int main(int argc, char **argv)
 9 {
10     if(argc != 3) {
11         printf("using %s <ip address> <message>\n", argv[0]);
12         return 0;
13     }
14     /*创建套接口*/
15     int fd = 0;
16     fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
17     if(fd < 0) {
18         perror("socket");
19         return 1;    
20     }
21 
22     /*服务器信息*/
23     struct sockaddr_in ser;
24     ser.sin_family = AF_INET;
25     ser.sin_port = htons(PORT);
26     ser.sin_addr.s_addr = inet_addr(argv[1]);
27 
28     /*创建链接*/
29     int ret = connect(fd, (struct sockaddr *)&ser, 16);
30     if(ret < 0) {
31         perror("connect");
32         return 1;
33     }        
34     
35     /*访问*/
36     ret = send(fd, argv[2], strlen(argv[2]), 0);
37     if(ret < 0) {
38         perror("send");
39         return 1;
40     }
41     
42     char buf[1024] = {0};
43     ret = recv(fd, buf, 1024, 0);
44     if(ret < 0) {
45         perror("recv");
46         return 1;
47     }    
48     printf("server: %s\n", buf);
49     close(fd);
50 
51     return 0;            
52 }
client

  上面代码需要注意的是,监听程序最大允许接受10个连接请求,如果这十个一直连接不断开的话,后续的连接请求就无法得到处理,所以我们需要在每次请求完毕之后就关闭nfd;下次请求再重新连接;

  第三个程序我们实现基于tcp的聊天程序:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<sys/socket.h>
 5 #include<netinet/in.h>
 6 
 7 int main()
 8 {
 9     /*创建套接口*/
10     int  fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
11     if(fd < 0){
12         perror("socket");
13         exit(EXIT_FAILURE);
14     }
15     
16     /*服务端信息*/
17     struct sockaddr_in srv;
18     srv.sin_family = AF_INET;
19     srv.sin_port=htons(9527);
20     srv.sin_addr.s_addr = htonl(INADDR_ANY);
21 
22     /*绑定*/
23     int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr ));
24     if(ret < 0){
25         perror("bind");
26         exit(EXIT_FAILURE);
27     }
28     ret = listen(fd,10);
29     if(ret < 0){
30         perror("bind");
31         exit(EXIT_FAILURE);
32     }
33     struct sockaddr_in snd;
34     int snd_len = 0;
35 
36     /*接受*/
37     int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len);
38     if(nfd < 0){
39         perror("accpet");
40         exit(EXIT_FAILURE);
41     }
42 
43     char data[1024] = {0};
44     char revdata[1024] = {0};
45     /*聊天*/
46     while(1){
47         memset(revdata, 0, 1024);    
48         memset(data, 0, 1024);         
49         ret = recv(nfd,revdata,1024,0);
50             if(ret < 0){
51               perror("recv");
52               exit(EXIT_FAILURE);
53            }
54            
55         printf("client say: %s\n",revdata);
56         if(strcmp(revdata, "end") == 0) {
57             break;
58         }
59 
60         ret = read(0,data,1024);
61            if(ret < 0){
62             perror("read");
63             exit(EXIT_FAILURE);
64         }
65         ret = send(nfd,data,1024,0);
66         if(ret < 0){
67             perror("recv");
68             exit(EXIT_FAILURE);
69         }
70         
71         
72     }    
73     close(nfd);
74     close(fd);
75     return 0;
76 }
server
 1 #include <stdio.h>
 2 #include <sys/socket.h>
 3 #include <netinet/in.h>
 4 #include <string.h>
 5 #include <sys/types.h>
 6 #include <unistd.h>
 7 
 8 int main()
 9 {
10     /*创建套接口*/
11     int sock_fd = 0;
12     sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
13     if(sock_fd < 0) {
14         perror("socket");
15         return 1;
16     }
17     /*服务端信息*/
18     struct sockaddr_in ser;
19     ser.sin_family = AF_INET;
20     ser.sin_port = htons(9527);
21     ser.sin_addr.s_addr = inet_addr("192.168.1.10");
22 
23     /*建立链接*/
24     int ret = connect(sock_fd, (struct sockaddr *)&ser, 16);
25     if(ret == -1) {
26         perror("connect");
27         return 1;
28     }
29 
30     /*聊天*/
31     unsigned char data[1024] = {0};
32     unsigned char rec[1024] = {0};
33     while(1) {    
34         memset(data, 0, 1024);
35         memset(rec, 0, 1024);
36         int r_size = read(0, data, 1024);
37         if(r_size < 0) {
38             perror("read");
39             return 1;
40         }
41 
42         ret = send(sock_fd, data, strlen(data), 0);
43         if(ret < 0) {
44             perror("send");
45             return 1;
46         }
47         
48         ret = recv(sock_fd, rec, 1024, 0);
49         if(ret < 0) {
50             perror("recv");
51             return 1;
52         }
53         printf("server said: %s\n", rec);
54     }
55     close(sock_fd);    
56     return 0;
57 }
client

上面这个代码存在的缺陷是,发送方跟接收只能发送一句接收一句,不能一次性发送多句,要解决这个问题就要需用到IO多路服用,可以通过这个函数来实现:
  int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

下面贴出代码:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<sys/socket.h>
 5 #include<netinet/in.h>
 6 
 7 int main()
 8 {
 9     /*创建套接口*/
10     int  fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
11     if(fd < 0){
12         perror("socket");
13         exit(EXIT_FAILURE);
14     }
15     
16     /*服务端信息*/
17     struct sockaddr_in srv;
18     srv.sin_family = AF_INET;
19     srv.sin_port=htons(9527);
20     srv.sin_addr.s_addr = htonl(INADDR_ANY);
21 
22     /*绑定*/
23     int ret = bind(fd,(struct sockaddr *)&srv,sizeof(struct sockaddr ));
24     if(ret < 0){
25         perror("bind");
26         exit(EXIT_FAILURE);
27     }
28     ret = listen(fd,10);
29     if(ret < 0){
30         perror("bind");
31         exit(EXIT_FAILURE);
32     }
33     struct sockaddr_in snd;
34     int snd_len = 0;
35 
36     /*接受*/
37     int nfd = accept(fd,(struct sockaddr *)&snd,&snd_len);
38     if(nfd < 0){
39         perror("accpet");
40         exit(EXIT_FAILURE);
41     }
42 
43     fd_set rfds;    
44     char data[1024] = {0};
45     char revdata[1024] = {0};
46     /*聊天*/
47     while(1){
48 
49         FD_ZERO(&rfds);
50         FD_SET(0,&rfds);
51         FD_SET(nfd,&rfds);
52         ret = select(nfd+1,&rfds,NULL,NULL,NULL);
53         if(FD_ISSET(nfd, &rfds)){
54               ret = recv(nfd,revdata,1024,0);
55                 if(ret < 0){
56                     perror("recv");
57                    exit(EXIT_FAILURE);
58                 }
59                 
60             printf("client say: %s\n",revdata);
61             if(strcmp(revdata, "end") == 0) {
62                 break;
63             }
64         }
65         if(FD_ISSET(0, &rfds)) {
66             ret = read(0,data,1024);
67                if(ret < 0){
68                 perror("read");
69                 exit(EXIT_FAILURE);
70             }
71             ret = send(nfd,data,1024,0);
72             if(ret < 0){
73                 perror("recv");
74                 exit(EXIT_FAILURE);
75             }
76         }
77         memset(revdata, 0, 1024);
78         memset(data, 0, 1024);
79     }    
80     close(nfd);
81     close(fd);
82     return 0;
83 }
server

 

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    /*创建套接口*/
    int sock_fd = 0;
    sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sock_fd < 0) {
        perror("socket");
        return 1;
    }
    /*服务端信息*/
    struct sockaddr_in ser;
    ser.sin_family = AF_INET;
    ser.sin_port = htons(9527);
    ser.sin_addr.s_addr = inet_addr("192.168.1.10");

    /*建立链接*/
    int ret = connect(sock_fd, (struct sockaddr *)&ser, 16);
    if(ret == -1) {
        perror("connect");
        return 1;
    }

    /*聊天*/
    fd_set rfds;    
    unsigned char data[1024] = {0};
    unsigned char rec[1024] = {0};
    while(1) {    
        memset(data, 0, 1024);
        memset(rec, 0, 1024);
        FD_ZERO(&rfds); //清空
        FD_SET(0,&rfds);//(标准输入)
        FD_SET(sock_fd,&rfds);//添加监听描述符(套接字)
        /*多路复用IO*/
        ret = select(sock_fd+1,&rfds,NULL,NULL,NULL);

        if(FD_ISSET(0, &rfds)){//监听键盘是否有输入,执行接收
            int r_size = read(0, data, 1024);
            if(r_size < 0) {
                perror("read");
                return 1;
            }
            ret = send(sock_fd, data, strlen(data), 0);
            if(ret < 0) {
                perror("send");
                return 1;
            }
        }
        
        if(FD_ISSET(sock_fd, &rfds)) {//监听sock_fd是否有输入,执行接收
            ret = recv(sock_fd, rec, 1024, 0);
            if(ret < 0) {
                perror("recv");
                return 1;
            }
            printf("server said: %s\n", rec);
        }
    }
    close(sock_fd);    
    return 0;
}
client

 

    

 

 

 

 

  

posted @ 2015-08-29 10:17  zhangwju  阅读(460)  评论(0编辑  收藏  举报