Linux socket 基础函数及使用范例

来源:互联网 发布:大数据 云计算 关系 编辑:程序博客网 时间:2024/05/17 09:36
以c语言开发的socket通信

首先Linux环境下的服务器端:

#include<sys/socket.h>int socket(int domain,int type,int protocol);
说明:
1 成功时返回一个整型的Socket描述符,失败时返回-1
2 domain:套接字中使用的协议族信息(protocol family)
常用的两个:PF_INET :IPv4互联网协议族
PF_INET6:IPv6互联网协议族
3 type:套接字类型(数据传输方式)
具有代表性的两个:
SOCK_STREAM:面相连接的套接字,流式,面向连接的比特流,顺序、可靠、双向,用于TCP通信
SOCK_DGRAM :面向消息的套接字,数据报式,无连接的,定长、不可靠、常用语于UDP通信
4 protocol:最终决定采用的协议。一般情况下为0;除非同一个协议族中存在多个传输方式相同的协议时,需要具体指定

例子:
1 IPv4协议族中面向连接的套接字(TCP套接字)

int tcp_socket = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
2 IPv4协议族中面向消息的套接字(UDP套接字)
int udp_socket = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);


int bind(int sockfd,struct sockaddr *myaddr,socklen_t addrlen);
说明:
1 将套接字与指定端口相连,成功返回0,失败返回-1
2 sockfd:前面调用socket()函数返回的文件描述符
3 struct sockaddr *myaddr:指向sockaddr结构体的指针(该结构体中保存有端口和IP地址信息)
4 socklen_t addrlen:指定了以addr所指向的地址结构体的字节长度


int listen(int sockfd,int backlog);
说明:
1 监听连接请求,成功返回0,失败返回-1
2 sockfd 处于监听状态,并且最多允许有backlog个客户端处于连接待状态

int accept(int sockfd,struct sockaddr *myaddr,socklen_t *addrlen)
说明:
1 服务器调用accept()接受连接,如果没有客户端的连接请求就阻塞等待直到有客户端连接上
2 成功则返回一个新的套接字用于连接,与客户端进行点对点的通信
3 sockfd:用于监听的文件描述符,即第一次socket()函数产生的
4 sockaddr *myaddr:传出参数,存有连接好的客户端的IP地址和端口信息,设为NULL表示不关心
5 sockaddr *addrlen:传入传出参数,传入的是调用者提供的缓冲区myaddr的长度以避免缓冲区溢出问题。传出的是客户端
地址结构体的实际长度(有可能没有占满调用者提供的缓冲区)如果给cliaddr参数传NULL表示不关心客户端的地址

int close(int filedes);
说明:关闭套接字

其次,客户端:

int socket(int domain,int type,int protocol);
同上:

int connect(int sockfd,struct sockaddr *serv_addr,socklen_t addrlen);
说明:
1 连接服务器,成功返回0,失败返回-1
2 struct sockaddr *serv_addr:服务器的IP地址和端口信息

int close(int filedes);

对于Linux而言,socket是文件的一种,因此读写文件同样适用于socket
写文件:
#include<unistd.h>//typedef unsigned int size_t//typedef signe4 int ssize_t ssize_t write(int fd,const void *buf,size_t nbytes);


说明:
1 成功返回写入的字节数,失败返回-1
2 int fd:文件描述符
3 const void *buf:保存写入数据的缓冲区
4 size_t nbytes:要传输数据的字节数

读文件:

ssize_t read(int fd,void *buf,size_t nbytes);
1 成功返回接受的字节数(但遇到文件结尾则返回0),失败则返回-1
2 int fd:文件描述符
3 const void *buf:保存接收数据的缓冲区
4 size_t nbytes:要接受数据的最大字节数

收发数据函数:

TCP:

int send(SOCKET s,const char FAR *buf,int len,int flags); 
说明:
1 SOCKET s:接受套接字
2 const char FAR *buf:发送数据缓冲区
3 int len:实际发送的长度(字节数)
4 int flags:该参数一般置0;
其他参数:
MSG_DONTROUTE:绕过路由表查找
MSG_DONTWAIT:此操作非阻塞
MSG_OOB:发送带外数据
5 成功则返回实际发送字节数,失败则返回SOCKET_ERROR


int recv(SOCKET s,char FAR *char,int len,int flags);
说明:
1 SOCKET s:接受套接字
2 char FAR * char:接受缓冲区
3 int len:接受缓冲区长度
4 int flags:该参数一般置0;
其他参数:
MSG_DONTWAIT:此操作非阻塞
MSG_OOB:接受带外数据
MSG_PEEK:窥看外来信息
MSG_WAITALL:等待所有数据
5 成功则返回实际发送字节数,失败则返回SOCKET_ERROR

UDP:int sendto(int sockfd,const void *msg,int len unsigned int flags,const struct sockaddr *to,int tolen);
说明:
1 该函数比send()多了两个参数:
2 const struct sockaddr *to:指向目的机的IP地址和端口信息
3 int tolen:struct sockaddr 的大小

int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr *from,int *fromlen);
说明:
1 int sockfd:接受套接字
2 void *buf:接受缓冲区
3 int len:接受缓冲区长度
4 unsigned int flags:同recv()
5 struct sockaddr *from:信息来源机的地址信息
6 int *fromlen:struct sockaddr的大小

使用范例:

//Server#include<stdio.h>#include<stdlib.h>#incldue<string.h>#incldue<unistd>#incldue<arpa/inet.h>#incldue<sys/socket.h>void error_handing(char *message);int main(int argc,char *argv[]){int serv_sock;int clnt_sock;struct sockaddr_in serv_addr;struct sockaddr_in clnt_addr;socklen_t clnt_addr_size;char message[] = "Hello World!";if(argc != 2)//提示输入端口{printf("Usage :%s <port>\n",argv[0]);exit(1);}serv_sock = socket(PF_INET,SOCK_STREAM,0);if(serv_sock == -1)error_handing("socket() error!\n");memset(serv_addr,0,sizeof(serv_addr)); //void *memset(void *s,int c,size_t n)//将已开辟内存空间 s 的首 n 个字节的值设为值 cserv_addr.family = AF_INET;serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);serv_addr.sin_port = htons(atoi(argv[1]));if(bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) == -1)error_handing("bind() error");if(listen(serv_sock,5) == -1)error_handing("listen() error");clnt_addr_size = sizeof(clnt_addr);clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_size);if(clnt_sock == -1)error_handing("accept() error");write(clnt_sock,message,sizeof(message));return 0;}void error_handing(char *message){fputs(message,stderr); //stderr为标准错误输出,在屏幕上输出由当前文件名和这条调用所在的行号组成的信息fputc('\n',stderr);exit(1);}


//Client#include<stdio.h>#include<stdlib.h>#incldue<string.h>#incldue<unistd>#incldue<arpa/inet.h>#incldue<sys/socket.h>void error_handing(char *message);int main(int argc,char* argv[]){int sock;struct sockaddr_in serv_addr;char message[30];int str_len;if(argc != 3){printf("Usage :%s <IP><Port>\n",argv[0]);exit(1);}sock = socket(PF_INET,SOCK_STREAM,0);if(sock == -1)error_handing("sock error!\n");memset(&serv_addr,0,sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port = htons(atoi(argv[2]);if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) == -1)error_handing("connect() error!\n");str_len = read(sock,message,sizeof(message)-1);if(str_len == -1)error_handing("read error!\n");printf("Message from server: %s \n",message);close(sock);return 0;}void error_handing(char *message){fputs(message,stderr);fputc('\n',stderr);exit(1);}


注:

struct sockaddr_in //该结构体针对Ipv4{sa_family_t sin_family;uint16_tsin_port;struct in_addr  sin_addr;char sin_zero[8];}struct in_addr{In_addr_ts_addr;}struct sockaddr   //此结构不止针对Ipv4{sa_family_tsin_family;charsa_data[14];}
网络字节序,即大端序,网络传输统一用大端序
htons htonl ntohl ntohs


字符串转32位大端序整型值
in_addr_t inet_addr(const char * string);

在转换的同时自动加入sin_addr结构体中char* addr = "127.232.124.79"struct sockaddr_in addr_inetinet_aton(addr,&addr_inet.sin_addr)

//WSAStringToAddress、WSAAddressToString用于windows中IP地址与字符串的转换#include "stdafx.h"#include<stdio.h>#include<stdlib.h>#include<WinSock2.h>int _tmain(int argc, _TCHAR* argv[]){char *strAddr = "196.168.1.154:8000";char strAddrBuff[50];SOCKADDR_IN servAddr;int size;WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);size = sizeof(servAddr);WSAStringToAddress(strAddr, AF_INET, NULL, (SOCKADDR*)&servAddr, &size);size = sizeof(strAddrBuff);WSAAddressToString((SOCKADDR*)&servAddr, sizeof(servAddr), NULL, strAddrBuff,(DWORD*)&size);printf("Second conv result:%s \n", strAddrBuff);WSACleanup();system("pause");return 0;}//accept()在客户端connect()之前为阻塞状态。


0 0