PS:TCP/UDP对比:
1. TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3. TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的 UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5. TCP首部开销20字节;UDP的首部开销小,只有8个字节
6. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
一、服务端编写思路流程
1.socket()创建套接字
2.bind()为套接字添加信息(IP地址和端口号)
3.listen()监听网络连接
4.accept()监听到有客户端接入,接受一个连接
5.read()、write()数据交互
二、服务端代码实现
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
// int socket(int domain, int type, int protocol);
// int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// int listen(int sockfd, int backlog);
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// void *memset(void *s, int c, size_t n);
int main(int argc, char **argv[])
{
int s_fd;
int c_fd;
int clen;
int n_read;
char readBuf[128];
char *msg = "I get your connect";
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr, 0, sizeof(struct sockaddr_in));
memset(&c_addr, 0, sizeof(struct sockaddr_in));
//1. socket(创建套接字)
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1){
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &s_addr.sin_addr);
//2. bind(为套接字添加信息, ip地址和端口号)
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
//3. listen(监听网络连接)
listen(s_fd, 10);
//4. accept(监听到有客户端接入,接受一个连接)
clen = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
if (c_fd == -1){
perror("accept");
}
else {
printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
}
//5. read(读数据)
memset(readBuf, 0, 128);
n_read = read(c_fd, readBuf, 128);
if (n_read == -1){
perror("read");
}
printf("get message: %d, %s\n", n_read, readBuf);
//6. wiite(写数据)
write(c_fd, msg, strlen(msg));
}
return 0;
}
~
三、客户端代码实现
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
// int socket(int domain, int type, int protocol);
// int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// void *memset(void *s, int c, size_t n);
int main(int argc, char **argv[])
{
int c_fd;
int clen;
int n_read;
char readBuf[128];
memset(readBuf, 0, 128); //memset(msg, 0, sizeof(msg),否则服务端读取数据会有乱码memset(msg, 0, sizeof(msg))
char *msg = "get message from client";
struct sockaddr_in c_addr;
memset(&c_addr, 0, sizeof(struct sockaddr_in));
//1. socket(创建套接字)
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if (c_fd == -1){
perror("socket");
exit(-1);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &c_addr.sin_addr);
//2. connect(客户端连接)
if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1){
perror("connect");
exit(-1);
}
//5. send(发数据)
write(c_fd, msg, strlen(msg));
//6. read(读数据)
n_read = read(c_fd, readBuf, 128);
if (n_read == -1){
perror("read");
}
else {
printf("get message from server: %d, %s\n", n_read, readBuf);
}
return 0;
}
四、Linux终端运行结果
五、实现双方聊天
1.服务端代码
o.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
// int socket(int domain, int type, int protocol);
// int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// int listen(int sockfd, int backlog);
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// void *memset(void *s, int c, size_t n);
int main(int argc, char **argv)
{
int s_fd;
int c_fd;
int clen;
int n_read;
int mark = 0;
char readBuf[128];
// char *msg = "I get your connect";
char msg[128] = {0};
if (argc != 3){
printf("the param is not good\n");
exit(-1);
}
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr, 0, sizeof(struct sockaddr_in));
memset(&c_addr, 0, sizeof(struct sockaddr_in));
//1. socket(创建套接字)
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1){
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &s_addr.sin_addr);
//2. bind(为套接字添加信息, ip地址和端口号)
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
//3. listen(监听网络连接)
listen(s_fd, 10);
//4. accept(监听到有客户端接入,接受一个连接)
clen = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
if (c_fd == -1){
perror("accept");
}
else {
printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
}
mark++;
if (fork() == 0){
if (fork() == 0){
while(1){
memset(msg, 0, sizeof(msg));
printf("input: ");
fgets(msg, 10, stdin);
write(c_fd, msg, strlen(msg));
}
}
while(1){
memset(readBuf, 0, sizeof(readBuf));
n_read = read(c_fd, readBuf, 128);
if (n_read == -1){
perror("read");
}
printf("get message: %d, %s\n", n_read, readBuf);
}
break;
}
}
return 0;
}
2.客户端代码
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
// int socket(int domain, int type, int protocol);
// int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// void *memset(void *s, int c, size_t n);
int main(int argc, char **argv)
{
int c_fd;
int clen;
int n_read;
char readBuf[128];
// char *msg = "get message from client";
char msg[128] = {0};
if (argc != 3){
printf("the param is not good\n");
exit(-1);
}
struct sockaddr_in c_addr;
memset(&c_addr, 0, sizeof(struct sockaddr_in));
//1. socket(创建套接字)
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if (c_fd == -1){
perror("socket");
exit(-1);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &c_addr.sin_addr);
//2. connect(客户端连接)
if (connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr_in)) == -1){
perror("connect");
exit(-1);
}
while(1){
if (fork() == 0){
while(1){
memset(msg, 0, sizeof(msg));
printf("input: ");
fgets(msg, 10, stdin);
write(c_fd, msg, strlen(msg));
}
}
while(1){
memset(readBuf, 0, sizeof(readBuf));
n_read = read(c_fd, readBuf, 128);
if (n_read == -1){
perror("read");
}
else {
printf("get message from server: %d, %s\n", n_read, readBuf);
}
}
}
return 0;
}
3.运行结果
ps:如果开启多个客户端时,服务端给客户端发送数据,客户端接收读取数据时会存在竞争,为了避免这种情况服务端就不要使用fgets(),只允许客户端给服务端键盘输入发送数据,服务端负责就收该数据即可,服务端代码如下:
// int socket(int domain, int type, int protocol);
// int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// int listen(int sockfd, int backlog);
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// void *memset(void *s, int c, size_t n);
int main(int argc, char **argv)
{
int s_fd;
int c_fd;
int clen;
int n_read;
int mark = 0;
char readBuf[128];
// char *msg = "I get your connect";
char msg[128] = {0};
if (argc != 3){
printf("the param is not good\n");
exit(-1);
}
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
memset(&s_addr, 0, sizeof(struct sockaddr_in));
memset(&c_addr, 0, sizeof(struct sockaddr_in));
//1. socket(创建套接字)
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if (s_fd == -1){
perror("socket");
exit(-1);
}
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &s_addr.sin_addr);
//2. bind(为套接字添加信息, ip地址和端口号)
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
//3. listen(监听网络连接)
listen(s_fd, 10);
//4. accept(监听到有客户端接入,接受一个连接)
clen = sizeof(struct sockaddr_in);
while(1){
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
if (c_fd == -1){
perror("accept");
}
else {
printf("get connect: %s\n", inet_ntoa(c_addr.sin_addr));
}
mark++;
if (fork() == 0){
if (fork() == 0){
while(1){
sprintf(msg, "welcom No.%d client", mark);
write(c_fd, msg, strlen(msg));
sleep(3);
}
}
while(1){
memset(readBuf, 0, sizeof(readBuf));
n_read = read(c_fd, readBuf, 128);
if (n_read == -1){
perror("read");
}
printf("get message: %d, %s\n", n_read, readBuf);
}
break;
}
}
return 0;
}
代码结果如下: