目录
创建套接字int socket(int domain, int type, int protocol);
为套接字添加信息(协议族、IP地址、端口号)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);
创建套接字int socket(int domain, int type, int protocol);
连接服务器int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
TCP与UDP的区别

字节序
- 大端字节序(BE):将高序字节存储在起始位置
- 小端字节序(LE):将低序字节存储在起始位置
- 大端字节序与小端字节序的区别:

- TCP协议使用大端字节序,需要将大端字节序转换为网络字节序

socket服务器
创建套接字int socket(int domain, int type, int protocol);
参数:
- int domain:选择协议族(一般使用AF_INET,互联网协议族)

- int type:选择协议

- int protocol:(通常为0, 表示type参数对应的默认协议)

返回值:
调用成功返回套接字描述符,调用失败返回-1
为套接字添加信息(协议族、IP地址、端口号)int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数:
- int sockfd:套接字
- const struct sockaddr *addr:sockaddr类型结构体地址,用来存储互联网协议族、IP地址和端口等信息
在IPv4因特网域中,存储套接字信息用sockaddr_in类型结构体,因此在传入bind()参数时要对该结构体进行强制转换,将sockaddr_in类型结构体强制转换成sockaddr类型结构体

将端口号转化成网络字节序:
将IP地址转化成网络字节序:
- socklen_t addrlen:参数二的内存空间大小
返回值:
调用成功返回0,调用失败返回-1
监听客户端的连接int listen(int sockfd, int backlog);
参数:
- int sockfd:套接字
- int backlog:指定在请求队列中允许的最大连接数
返回值:
调用成功返回0,调用失败返回-1
等待客户端的连接int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
当没有客户端连接时,accept()会阻塞等待
参数:
- int sockfd:套接字
- const struct sockaddr *addr:sockaddr类型结构体地址
- socklen_t *addrlen:参数二内存空间大小的值的地址

返回值:
调用成功返回套接字描述符,返回的套接字描述符用于传输数据,调用失败返回-1
向客户端发数据write()
跟文件编程中的write()没什么区别,使用accept()返回的套接字描述符
从客户端读数据read()
跟文件编程中的read()没什么区别,使用accept()返回的套接字描述符
实现
- 实现1:
主进程等待客户端的连接,当客户端连接后,fork()一个子进程不断读取客户端的数据,然后在该子进程里fork()一个子进程向客户端发数据
- 实现2:
主线程等待客户端的连接,当客户端连接后pthread_create()两个线程来处理,线程A用来读取客户端的数据,线程B用来给客户端发送数据
服务器(fork进程)实现:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <linux/in.h>
int main(int argc,char *argv[])
{
char writeBuf[128];
if(argc < 3){
printf("missing parameter ->IP or ->port number\n");
exit(-1);
}
int netID = socket(AF_INET,SOCK_STREAM,0);
if(netID < 0){
perror("socket");
exit(-1);
}
struct sockaddr_in my_net;
memset(&my_net,0,sizeof(struct sockaddr_in));
my_net.sin_family = AF_INET;
my_net.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&my_net.sin_addr);
if(bind(netID,(struct sockaddr *)&my_net,sizeof(struct sockaddr_in)) < 0){
perror("bind");
exit(-1);
}
if(listen(netID,10) < 0){
perror("listen");
exit(-1);
}
int msgID;
struct sockaddr_in msg_addr;
int tmp = 0;
int size = sizeof(struct sockaddr_in);
while(1){
memset(&msg_addr,0,sizeof(struct sockaddr_in));
msgID = accept(netID,(struct sockaddr *)&msg_addr,&size);
if(msgID < 0){
perror("accept");
}
tmp++;
printf("get connect%d to ID:%s\n",tmp,inet_ntoa(msg_addr.sin_addr));
if(fork() == 0){//创造子进程进行读取客户端的数据
if(fork() == 0){//创建子进程向客户端发送数据
while(1){
memset(writeBuf,'\0',sizeof(writeBuf));
fgets(writeBuf,128,stdin);
write(msgID,writeBuf,strlen(writeBuf));
}
exit(0);
}
int readSize = 0;
char readBuf[128];
while(1){
memset(readBuf,'\0',sizeof(readBuf));
readSize = read(msgID,readBuf,128);
if(readSize < 0){
perror("read");
}else{
printf("msg from is %s\n",readBuf);
}
}
exit(0);
}
}
return 0;
}
client客户端
创建套接字int socket(int domain, int type, int protocol);
参数:
- int domain:选择协议族(一般使用AF_INET,互联网协议族)

- int type:选择协议

- int protocol:(通常为0, 表示type参数对应的默认协议)

返回值:
调用成功返回套接字描述符,调用失败返回-1
连接服务器int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数:
- int sockfd:套接字
- const struct sockaddr *addr:sockaddr类型结构体地址,用来存储互联网协议族、IP地址和端口等信息
在IPv4因特网域中,存储套接字信息用sockaddr_in类型结构体,因此在传入bind()参数时要对该结构体进行强制转换,将sockaddr_in类型结构体强制转换成sockaddr类型结构体

将端口号转化成网络字节序:
将IP地址转化成网络字节序:
- socklen_t addrlen:参数二的内存空间大小
返回值:
调用成功返回0,调用失败返回-1
向客户端发数据write()
跟文件编程中的write()没什么区别,使用accept()返回的套接字描述符
从客户端读数据read()
跟文件编程中的read()没什么区别,使用accept()返回的套接字描述符
实现
连接服务器成功后,主进程不断读取服务器数据,fork()一个子进程往客户端发送数据
客户端实现
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <linux/in.h>
int main(int argc,char *argv[])
{
if(argc < 3){
printf("missing parameter ->IP or ->port number\n");
exit(-1);
}
int netID = socket(AF_INET,SOCK_STREAM,0);
if(netID < 0){
perror("socket");
exit(-1);
}
struct sockaddr_in my_net;
memset(&my_net,0,sizeof(struct sockaddr_in));
my_net.sin_family = AF_INET;
my_net.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&my_net.sin_addr);
if(connect(netID,(struct sockaddr *)&my_net,sizeof(struct sockaddr_in)) < 0){
perror("connect");
exit(-1);
}
printf("connect socket is ok\n");
char writeBuf[128];
while(1){
if(fork() == 0){
while(1){
memset(writeBuf,'\0',sizeof(writeBuf));
fgets(writeBuf,128,stdin);
write(netID,writeBuf,strlen(writeBuf));
}
exit(0);
}
int readSize = 0;
char readBuf[128];
while(1){
memset(readBuf,'\0',sizeof(readBuf));
readSize = read(netID,readBuf,128);
if(readSize < 0){
perror("read");
}else{
printf("msg from is socket:%s\n",readBuf);
}
}
exit(0);
}
return 0;
}
本文详细介绍了TCP与UDP两种传输协议的主要区别,包括它们的数据可靠性、连接方式和速度等方面。同时,深入讲解了Socket编程的基本步骤,如创建套接字、绑定地址、监听连接、接受连接以及数据的读写操作。通过示例代码展示了服务器如何使用fork进程或线程处理客户端连接,并提供了客户端的连接与通信实现。

1375

被折叠的 条评论
为什么被折叠?



