本实验是在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”,退出。下面是演示效果:

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