以C语言为例完成一个hello/hi的简单的网络聊天程序

自闭症网瘾萝莉.ら 提交于 2019-12-06 15:11:08

本实验是在ubuntu系统下实现一个简单的客户端和服务端通信的简单demo,以TCP协议为例。

首先介绍API:

server用到的API有socket,bind,listen,accept,read,write,close   (read和write可以用send和recv替换)

client用到的API有socket,connect,read,write,close    (read和write可以用send和recv替换)

下面来看一下各个API的声明和描述,用man查看各个api声明:

 

 

 

因为linux系统把一切都当做文件来看待,所以像操作文件一样来操作socket文件描述符,写入数据就是发送数据,读数据就是接收数据,这种做法符合linux系统的一切都当做文件来看待的设计理念。当然也可以用send和recv函数,read和write底层调用的跟send和recv的实现是一样的。

 

 

 上面就是各种API的介绍,总结来说;

socket函数主要是来定义是使用流数据还是数据报数据,对应的就是TCP和UDP协议,TCP的负载对传输层来说是没有数据结构的概念的,对于上层调用write函数,只是将数据放到发送缓冲区,每次write就只是把数据放到发送缓冲区的最后,就是排队放在后面,完全没有结构的概念,没有一次write是属于一个报文,而UDP就是一次发送就是一个报文,接收只能一个报文一个报文的接收。假如,存在一个客户端连续向服务端连续发送10次后才接收,TCP将会接收到所有数据,而UDP只会一个一个报文的接收,这就是流和数据报的区别。对应客户端来说,socket函数返回的是与服务端绑定的终结点文件描述符。

 

bind函数就是绑定一个IP和端口号,IP对应主机,不想设置IP的就设置INADDR_ANY,这会默认绑定本机的所有IP,IP就是对应主机,能找到主机就可以了,而端口号对应的特定的应用程序。

 

listen函数是设置最大等待连接的个数,超过设置的握手请求将会被reset。

 

accpet函数就是等待客户端连接,对应的是客户端的connect函数,这个过程就是三报文握手的过程,确定双发的收发能力正常。accept函数返回的是和客户端绑定的终结点文件描述符。

 

wirte和read函数就是发送和接收数据,write和read只是和内核交换数据,write函数是将数据拷贝到发送缓冲区,read函数是将内核的接收缓冲区拷贝到用户缓冲区。

 

close函数就四报文挥手过程。

 

下面是demo,代码很简单

server.c

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/socket.h>
 4 #include <unistd.h>
 5 #include <string.h>
 6 #include <netinet/in.h>
 7 #include <pthread.h>
 8 #include <stdlib.h>
 9 void *fun(void *arg)
10 {
11     int fd;
12     char sendbuf[64];
13     fd = *(int *)arg;
14     printf("Please input:\n");
15     while(1)
16     {
17         memset(sendbuf, 0, sizeof(sendbuf));
18         scanf("%s", sendbuf);
19         write(fd, sendbuf, strlen(sendbuf));
20         if(!strncmp(sendbuf, "quit", strlen("quit")))
21         {
22             close(fd);
23             exit(0);
24         }
25     }
26     return NULL;
27 }
28 
29 int main()
30 {
31     int socket_fd, client_fd;
32     int ret, len;
33     char recvbuf[64];
34     struct sockaddr_in server_addr, client_addr;
35     pthread_t tid;
36     socket_fd = socket(PF_INET, SOCK_STREAM, 0);
37 
38     if(socket_fd < 0)
39     {
40         perror("Error");
41         return -1;
42     }
43 
44     server_addr.sin_family = AF_INET;
45     server_addr.sin_addr.s_addr = INADDR_ANY;
46     server_addr.sin_port = htons(33333);
47 
48     ret = bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
49     if(ret < 0)
50     {
51         perror("Error");
52         return -1;
53     }
54 
55     listen(socket_fd, 5);
56 
57     len = sizeof(client_addr);
58     client_fd = accept(socket_fd, (struct sockaddr *)&client_addr, &len);
59     if(client_fd < 0)
60     {
61         perror("Error");
62         return -1;
63     }
64     pthread_create(&tid, NULL, fun, (void *)&client_fd);
65     while(1)
66     {
67         memset(recvbuf, 0, sizeof(recvbuf));
68         read(client_fd, recvbuf, sizeof(recvbuf));
69         if(!strncmp(recvbuf, "quit", strlen("quit")))
70         {
71             pthread_cancel(tid);
72             break;
73         }
74         printf("recv: %s\n", recvbuf);
75     }
76     
77     close(client_fd);
78     close(socket_fd);
79     return 0;
80 }

client.c

 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/socket.h>
 4 #include <netinet/in.h>
 5 #include <unistd.h>
 6 #include <string.h>
 7 #include <pthread.h>
 8 #include <stdlib.h>
 9 void *fun(void *arg)
10 {
11     int fd;
12     char sendbuf[64];
13     fd = *(int *)arg;
14     printf("Please input:\n");
15     while(1)
16     {
17         memset(sendbuf, 0, sizeof(sendbuf));
18         scanf("%s", sendbuf);
19         write(fd, sendbuf, strlen(sendbuf));
20         if(!strncmp(sendbuf, "quit", strlen("quit")))
21         {
22             close(fd);
23             exit(0);
24         }
25     }
26     return NULL;
27 }
28 
29 int main()
30 {
31     int socket_fd;
32     struct sockaddr_in server_addr;
33     char recvbuf[64];
34     int ret;
35     pthread_t tid;
36 
37     socket_fd = socket(PF_INET, SOCK_STREAM, 0);
38     if(socket_fd < 0)
39     {
40         perror("Error");
41         return -1;
42     }
43 
44     server_addr.sin_family = AF_INET;
45     server_addr.sin_port = htons(33333);
46     server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
47 
48     ret = connect(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
49     if(ret < 0)
50     {
51         perror("Error");
52         return -1;
53     }
54     
55     ret = pthread_create(&tid, NULL, fun, (void *)&socket_fd);
56     if(ret != 0)
57     {
58         perror("Error");
59         return -1;
60     }
61     while(1)
62     {
63         memset(recvbuf, 0, sizeof(recvbuf));
64         read(socket_fd, recvbuf, sizeof(recvbuf));
65         if(!strncmp(recvbuf, "quit", strlen("quit")))
66         {
67             pthread_cancel(tid);
68             break;
69         }
70         printf("recv: %s\n", recvbuf);
71     }
72     close(socket_fd);
73     return 0;
74 }

 

客户端和服务端都创建了一个线程用于向对方发送数据,主线程用来接收对方发来的数据。这样可以同时发送数据和接收数据。

当输入“quit”,退出。下面是演示效果:

 

 

 

 

 

以上是这次实验的内容,由于本人水平有限,如有错误之处,还请指正,谢谢!

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!