socket 第一篇初级详解

socket概述

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。Socket就是该模式的一个实现, socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭).
我理解socket就相当于一个接口用来传输数据的网络数据就是通过这个接口发出去

常用的接口函数

socket():socket创建函数
bind():服务常用函数绑定,本地的地址和端口
listen():TCP专用,开启监听模式
accept():TCP专用,从socket等客户端连接,多为阻塞状态当有连接来时才返回
connect():TCP专用,客服端连接服务器
send():TCP专用发送数据
recv():TCP专用接受数据
sendto():UDP专用,发送数据到指定IP和端口
recvfrom():UDP专用,接收数据,并返回发送端的IP地址和端口
close():关闭套接字socket

函数详解

socket()

原型:int socket (int domain, int type, int protocol)

功能描述:初始化创建socket对象,通常是第一个调用的socket函数。 成功时,返回非负数的socket描述符;失败是返回-1。socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。调用socket()函数时,socket执行体将建立一个socket,实际上”建立一个socket”意味着为一个socket数据结构分配存储空间。socket执行体为你管理描述符表。

参数解释:

domain – 指明使用的协议族。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

type – 指明socket类型,有3种:

                                             SOCK_STREAM -- TCP类型,保证数据顺序及可靠性;

                                             SOCK_DGRAM --  UDP类型,不保证数据接收的顺序,非可靠连接;

                                             SOCK_RAW -- 原始类型,允许对底层协议如IP或ICMP进行直接访问,不太常用。

protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0(系统自动选择)。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。protocol通常为0系统自动选择协议类型

错误返回:
WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
WSAENETDOWN:套接口实现检测到网络子系统失效。
WSAEAFNOSUPPORT:不支持指定的地址族。
WSAEINPROGRESS:一个阻塞的套接口调用正在运行中。
WSAEMFILE:无可用文件描述字。
WSAENOBUFS:无可用缓冲区,无法创建套接口。
WSAEPROTONOSUPPORT:不支持指定的协议。
WSAEPROTOTYPE:指定的协议不适用于本套接口。
WSAESOCKTNOSUPPORT:本地址族中不支持该类型套接口。

bind()

原型:int bind(int sockfd, const struct sockaddr* myaddr, socklen_t addrlen)

功能描述:将创建的socket绑定到指定的IP地址和端口上,通常是第二个调用的socket接口。返回值:0 – 成功,-1 – 出错。当socket函数返回一个描述符时,只是存在于其协议族的空间中,并没有分配一个具体的协议地址(这里指IPv4/IPv6和端口号的组合),bind函数可以将一组固定的地址绑定到sockfd上。

通常服务器在启动的时候都会绑定一个众所周知的协议地址,用于提供服务,客户就可以通过它来接连服务器;而客户端可以指定IP或端口也可以都不指定,未分配则系统自动分配。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

注意:

(1) 如果有多个可用的连接(多个IP),内核会根据优先级选择一个IP作为源IP使用。

(2) 如果socket使用bind绑定到特定的IP和port,则无论是TCP还是UDP,都会从指定的IP和port发送数据。

参数解释:

sockfd – socket()函数返回的描述符;

myaddr – 指明要绑定的本地IP和端口号,使用网络字节序,即大端模式(详见3.1)。

addrlen – 常被设置为sizeof(struct sockaddr)。

可以利用下边的赋值语句,自动绑定本地IP地址和随机端口:

my_addr.sin_port = 0; /* 系统随机选择一个未被使用的端口号 */
my_addr.sin_addr.s_addr = INADDR_ANY; /* 填入本机IP地址 */
另外要注意的是,当调用函数时,一般不要将端口号置为小于1024的值,因为1~1024是保留端口号,你可以使用大于1024中任何一个没有被占用的端口号。

错误返回:
WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
WSAENETDOWN:套接口实现检测到网络子系统失效。
WSAEADDRINUSE:所定端口已在使用中(参见setoption()中的SO_REUSEADDR选项)。
WSAEFAULT:namelen参数太小(小于sockaddr结构的大小)。
WSAEINPROGRESS:一个阻塞的套接口调用正在运行中。
WSAEAFNOSUPPORT:本协议不支持所指定的地址族。
WSAEINVAL:该套接口已与一个地址捆绑。
WSAENOBUFS:无足够可用缓冲区,连接过多。
WSAENOTSOCK:描述字不是一个套接口。

listen()

原型:int listen(int sockfd, int backlog)

功能描述:listen()函数仅被TCP类型的服务器程序调用,实现监听服务,它实现2件事情:

“1. 当socket()创建1个socket时,被假设为主动式套接字,也就是说它是一个将调用connect()发起连接请求的客户端套接字;函数listen()将套接口转换为被动式套接字,指示内核接受向此套接字的连接请求,调用此系统调用后tcp 状态机由close转换到listen。
2.第2个参数指定了内核为此套接字排队的最大连接个数。”

listen()成功时返回0,错误时返回-1。

参数解释:

sockfd – socket()函数返回的描述符;

backlog – 指定内核为此套接字维护的最大连接个数,包括“未完成连接队列–未完成3次握手”、“已完成连接队列–已完成3次握手,建立连接”。大多数系统缺省值为20。

错误返回:
WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
WSAENETDOWN:套接口实现检测到网络子系统失效。
WSAEADDRINUSE:试图用listen()去监听一个正在使用中的地址。
WSAEINPROGRESS:一个阻塞的套接口调用正在运行中。
WSAEINVAL:该套接口未用bind()进行捆绑,或已被连接。
WSAEISCONN:套接口已被连接。
WSAEMFILE:无可用文件描述字。
WSAENOBUFS:无可用缓冲区空间。
WSAENOTSOCK:描述字不是一个套接口。
WSAEOPNOTSUPP:该套接口不正常listen()调用。

accept()

原型:int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen)

功能描述:accept()函数仅被TCP类型的服务器程序调用,从已完成连接队列返回下一个建立成功的连接,如果已完成连接队列为空,线程进入阻塞态睡眠状态。成功时返回套接字描述符,错误时返回-1。

如果accpet()执行成功,返回由内核自动生成的一个全新socket描述符,用它引用与客户端的TCP连接。通常我们把accept()第一个参数成为监听套接字(listening socket),把accept()功能返回值成为已连接套接字(connected socket)。一个服务器通常只有1个监听套接字,监听客户端的连接请求;服务器内核为每一个客户端的TCP连接维护1个已连接套接字,用它实现数据双向通信。

参数解释:

sockfd – socket()函数返回的描述符;

addr – 输出一个的sockaddr_in变量地址,该变量用来存放发起连接请求的客户端的协议地址;

addrten – 作为输入时指明缓冲器的长度,作为输出时指明addr的实际长度。

错误返回:
WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
WSAENETDOWN:套接口实现检测到网络子系统失效。
WSAEFAULT:addrlen参数太小(小于socket结构的大小)。
WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。
WSAEINPROGRESS:一个阻塞的套接口调用正在运行中。
WSAEINVAL:在accept()前未激活listen()。
WSAEMFILE:调用accept()时队列为空,无可用的描述字。
WSAENOBUFS:无可用缓冲区空间。
WSAENOTSOCK:描述字不是一个套接口。
WSAEOPNOTSUPP:该套接口类型不支持面向连接服务。
WSAEWOULDBLOCK:该套接口为非阻塞方式且无连接可供接受。
WSAECONNRESET:接受连接后在accept返回之前,被远程客户端断开连接

connetct()

原型: int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)

功能描述:connect()通常由TCP类型客户端调用,用来与服务器建立一个TCP连接,实际是发起3次握手过程,连接成功返回0,连接失败返回1。

注意:

(1) 可以在UDP连接使用使用connect(),作用是在UDP套接字中记住目的地址和目的端口。
(2) UDP套接字使用connect后,如果数据报不是connect中指定的地址和端口,将被丢弃。没有调用connect的UDP套接字,将接收所有到达这个端口的UDP数据报,而不区分源端口和地址。

参数解释:

sockfd – 本地客户端额socket描述符;

serv_addr – 服务器协议地址;

addrlen – 地址缓冲区的长度。

错误返回:
WSAENOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
WSAENETDOWN:套接口实现检测到网络子系统失效。
WSAEADDRINUSE:所指的地址已在使用中。
WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。
WSAEINPROGRESS:一个阻塞的套接口调用正在运行中。
WSAEADDRNOTAVAIL:在本地机器上找不到所指的地址。
WSAENOTSUPPORT:所指族中地址无法与本套接口一起使用。
WSAECONNREFUSED:连接尝试被强制拒绝。
WSAEDESTADDREQ:需要目的地址。
WSAEFAULT:namelen参数不正确。
WSAEINVAL:套接口没有准备好与一地址捆绑。
WSAEISCONN:套接口早已连接。
WSAEMFILE:无多余文件描述字。
WSAENETUNREACH:当前无法从本主机访问网络。
WSAENOBUFS:无可用缓冲区。套接口未被连接。
WSAENOTSOCK:描述字不是一个套接口。
WSAETIMEOUT:超时时间到。
WSAEWOULDBLOCK:套接口设置为非阻塞方式且连接不能立即建立。可用select()调用对套接口写,因为select()时会进行连接。

send()

原型:int send(int sockfd, const void *msg, int len, int flags)

功能描述:TCP类型的数据发送。

每个TCP套接口都有一个发送缓冲区,它的大小可以用SO_SNDBUF这个选项来改变。调用send函数的过程,实际是内核将用户数据拷贝至TCP套接口的发送缓冲区的过程:若len大于发送缓冲区大小,则返回-1;否则,查看缓冲区剩余空间是否容纳得下要发送的len长度,若不够,则拷贝一部分,并返回拷贝长度(指的是非阻塞send,若为阻塞send,则一定等待所有数据拷贝至缓冲区才返回,因此阻塞send返回值必定与len相等);若缓冲区满,则等待发送,有剩余空间后拷贝至缓冲区;若在拷贝过程出现错误,则返回-1。关于错误的原因,查看errno的值。

如果send在等待协议发送数据时出现网络断开的情况,则会返回-1。注意:send成功返回并不代表对方已接收到数据,如果后续的协议传输过程中出现网络错误,下一个send便会返回-1发送错误。TCP给对方的数据必须在对方给予确认时,方可删除发送缓冲区的数据。否则,会一直缓存在缓冲区直至发送成功(TCP可靠数据传输决定的)。

参数解释:

sockfd – 发送端套接字描述符(非监听描述符)。

msg – 待发送数据的缓冲区。

len – 待发送数据的字节长度。

flags – 一般情况下置为0。

错误返回:
WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
WSAEACESS:要求地址为广播地址,但相关标志未能正确设置。
WSAEINTR:通过一个WSACancelBlockingCall()来取消一个(阻塞的)调用。
WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
WSAEFAULT:buf参数不在用户地址空间中的有效位置。
WSAENETRESET:由于WINDOWS套接口实现放弃了连接,故该连接必需被复位。
WSAENOBUFS:WINDOWS套接口实现报告一个缓冲区死锁。
WSAENOTCONN:套接口未被连接。
WSAENOTSOCK:描述字不是一个套接口。
WSAEOPNOTSUPP:已设置了MSG_OOB,但套接口非SOCK_STREAM类型。
WSAESHUTDOWN:套接口已被关闭。一个套接口以1或2的how参数调用shutdown()关闭后,无法再用send()函数。
WSAEWOULDBLOCK:
WSAEMSGSIZE:套接口为SOCK_DGRAM类型,且数据报大于WINDOWS套接口实现所支持的最大值。
WSAEINVAL:套接口未用bind()捆绑。
WSAECONNABORTED:由于超时或其他原因引起虚电路的中断。
WSAECONNRESET:虚电路被远端复位。

recv()

原型:int recv(int sockfd, void *buf, int len, unsigned int flags)

功能描述:TCP类型数据接收。recv()从接收缓冲区拷贝数据。成功时,返回拷贝的字节数,失败返回-1。阻塞模式下,recv/recvfrom将会阻塞到缓冲区里至少有一个字节(TCP)/至少有一个完整的UDP数据报才返回,没有数据时处于休眠状态。若非阻塞,则立即返回,有数据则返回拷贝的数据大小,否则返回错误-1。

注意:
(1)recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR;
(2)如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的接收数据是协议来完成的);
recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
(3) 读数据的时候需要考虑的是当recv()返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取。
(4) 返回值<0时并且(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)的情况下认为连接是正常的,继续接收。
只是阻塞模式下recv会阻塞着接收数据,非阻塞模式下如果没有数据会返回,不会阻塞着读,因此需要循环读取)。

参数详解:
sockfd :指定套接字描述符

buf:指明一个缓冲区,该缓冲区用来存放recv接收到的数据

len:指明缓冲区长度

flags:一般设为0可以设为的只有:
MSG_DONTROUTE 绕过路由表查找。
MSG_DONTWAIT 仅本操作非阻塞。
MSG_OOB 发送或接收带外数据。
MSG_PEEK 窥看外来消息。
MSG_WAITALL 等待所有数据。

错误返回:
EAGAIN:套接字已标记为非阻塞,而接收操作被阻塞或者接收超时
EBADF:sock不是有效的描述词
ECONNREFUSE:远程主机阻绝网络连接
EFAULT:内存空间访问出错
EINTR:操作被信号中断
EINVAL:参数无效
ENOMEM:内存不足
ENOTCONN:与面向连接关联的套接字尚未被连接上
ENOTSOCK:sock索引的不是套接字

sendto()

原型:int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *dst_addr, int addrlen)

功能描述:用于非可靠连接(UDP)的数据发送,因为UDP方式未建立连接socket,因此需要制定目的协议地址。

当本地与不同目的地址通信时,只需指定目的地址,可使用同一个UDP套接口描述符sockfd,而TCP要预先建立连接,每个连接都会产生不同的套接口描述符,体现在:客户端要使用不同的fd进行connect,服务端每次accept产生不同的fd。

因为UDP没有真正的发送缓冲区,因为是不可靠连接,不必保存应用进程的数据拷贝,应用进程中的数据在沿协议栈向下传递时,以某种形式拷贝到内核缓冲区,当数据链路层把数据传出后就把内核缓冲区中数据拷贝删除。因此它不需要一个发送缓冲区。写UDP套接口的sendto/write返回表示应用程序的数据或数据分片已经进入链路层的输出队列,如果输出队列没有足够的空间存放数据,将返回错误ENOBUFS.

参数解释:

sockfd – 发送端套接字描述符(非监听描述符);

msg – 待发送数据的缓冲区;

len – 待发送数据的字节长度;

flags – 一般情况下置为0;

dst_addr – 数据发送的目的地址;

addrlen – 地址长度。

错误返回:
EBADF 参数s非法的socket处理代码。
EFAULT 参数中有一指针指向无法存取的内存空间。
WNOTSOCK 参数 s为一文件描述词,非socket。
EINTR 被信号所中断。
EAGAIN 此动作会令进程阻断,但参数s的socket为不可阻断的。
ENOBUFS 系统的缓冲内存不足。
EINVAL 传给系统调用的参数不正确。

recvfrom()

原型:int recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, int*fromlen)

功能描述:用于非可靠连接(UDP)的数据接收。

参数解释:

sockfd – 接收端套接字描述;

buf – 用于接收数据的应用缓冲区地址;

len – 指名缓冲区大小;

flags – 调用操作方式。是以下一个或者多个标志的组合体,可通过“ | ”操作符连在一起(通常设为0):
MSG_DONTWAIT:操作不会被阻塞。
MSG_ERRQUEUE: 指示应该从套接字的错误队列上接收错误值,依据不同的协议,错误值以某种辅佐性消息的方式传递进来,使用者应该提供足够大的缓冲区。导致错误的原封包通过msg_iovec作为一般的数据来传递。导致错误的数据报原目标地址作为msg_name被提供。错误以sock_extended_err结构形态被使用,定义如下
#define SO_EE_ORIGIN_NONE 0
#define SO_EE_ORIGIN_LOCAL 1
#define SO_EE_ORIGIN_ICMP 2
#define SO_EE_ORIGIN_ICMP6 3
struct sock_extended_err
{
u_int32_t ee_errno;
u_int8_t ee_origin;
u_int8_t ee_type;
u_int8_t ee_code;
u_int8_t ee_pad;
u_int32_t ee_info;
u_int32_t ee_data;
};
MSG_PEEK:指示数据接收后,在接收队列中保留原数据,不将其删除,随后的读操作还可以接收相同的数据。
MSG_TRUNC:返回封包的实际长度,即使它比所提供的缓冲区更长, 只对packet套接字有效。
MSG_WAITALL:要求阻塞操作,直到请求得到完整的满足。然而,如果捕捉到信号,错误或者连接断开发生,或者下次被接收的数据类型不同,仍会返回少于请求量的数据。
MSG_EOR:指示记录的结束,返回的数据完成一个记录。
MSG_TRUNC:指明数据报尾部数据已被丢弃,因为它比所提供的缓冲区需要更多的空间。
MSG_CTRUNC:指明由于缓冲区空间不足,一些控制数据已被丢弃。
MSG_OOB:指示接收到out-of-band数据(即需要优先处理的数据)。
MSG_ERRQUEUE:指示除了来自套接字错误队列的错误外,没有接收到其它数据。

src_addr – 数据来源端的地址;

fromlen – 作为输入时,fromlen常置为sizeof(struct sockaddr);当输出时,fromlen包含实际存入buf中的数据字节数。

错误返回:
EBADF 参数s非合法的socket处理代码
EFAULT 参数中有一指针指向无法存取的内存空间。
ENOTSOCK 参数s为一文件描述词,非socket。
EINTR 被信号所中断。
EAGAIN 此动作会令进程阻断,但参数s的socket为不可阻断。
ENOBUFS 系统的缓冲内存不足
ENOMEM 核心内存不足
EINVAL 传给系统调用的参数不正确。

TCP 简单socket通信

服务器

server.c

#include    <sys/types.h>   /* basic system data types */
#include    <sys/socket.h>  /* basic socket definitions */
#include    <sys/time.h>    /* timeval{} for select() */
#include    <time.h>        /* timespec{} for pselect() */
#include    <sys/time.h>    /* includes <time.h> unsafely */
#include    <time.h>        /* old system? */
#include    <netinet/in.h>  /* sockaddr_in{} and other Internet defns */
#include    <arpa/inet.h>   /* inet(3) functions */
#include    <errno.h>
#include    <fcntl.h>       /* for nonblocking */
#include    <netdb.h>
#include    <signal.h>
#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <sys/stat.h>    /* for S_xxx file mode constants */
#include    <sys/uio.h>     /* for iovec{} and readv/writev */
#include    <unistd.h>
#include    <sys/wait.h>
#include    <sys/un.h>      /* for Unix domain sockets */
#include    <sys/select.h>  /* for convenience */
#include    <sys/param.h>   /* OpenBSD prereq for sysctl.h */
#include    <sys/sysctl.h>

#define MAXLINE 1024
#define LLINK  100
#define PORT 8888
#define SA  struct sockaddr

int main(int agrc,char *argv)
{
    int sockfd,connfd;//本地套接字
    char buffsend[]="welcome to connect Doream_dog Server";
    char buff[MAXLINE];
    struct sockaddr_in servaddr;//服务器地址
    struct sockaddr_in clienaddr;//客服端地址
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
        {
        printf("error is sockfd create\n");
        exit(0);
        }
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(PORT);//设置端口
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);//设置IP地址为本地
    if(bind(sockfd, (SA*) &servaddr, sizeof(servaddr))<0)
        {
        printf("error is bind\n");
        exit(0);
        }
    listen(sockfd,LLINK);//监听套接字
    printf("server crearte successful\n");
    while(1)
    {
        socklen_t len = sizeof(struct sockaddr_in);
         bzero(&clienaddr,sizeof(clienaddr));
        connfd=accept(sockfd,(SA*)&clienaddr,&len);
        if(connfd==-1)
            {
            printf("error is accept\n");
                exit(0);
            } 
    printf("weoclome iP: %s port: %u connetct\n",inet_ntoa(clienaddr.sin_addr),ntohs(clienaddr.sin_port));

    bzero(buff,sizeof(buff));
    read(connfd,buff,MAXLINE);
    printf("recv client %d message: %s len:%d\n",connfd,buff,strlen(buff));//打印客服端的消息

    send(connfd, buffsend,MAXLINE, 0);//发送一个消息给客服端

    close(connfd);//关闭连接的套接字
    }
  return 0;   
}

客服端

client.c

#include    <sys/types.h>   /* basic system data types */
#include    <sys/socket.h>  /* basic socket definitions */
#include    <sys/time.h>    /* timeval{} for select() */
#include    <time.h>        /* timespec{} for pselect() */
#include    <sys/time.h>    /* includes <time.h> unsafely */
#include    <time.h>        /* old system? */
#include    <netinet/in.h>  /* sockaddr_in{} and other Internet defns */
#include    <arpa/inet.h>   /* inet(3) functions */
#include    <errno.h>
#include    <fcntl.h>       /* for nonblocking */
#include    <netdb.h>
#include    <signal.h>
#include    <stdio.h>
#include    <stdlib.h>
#include    <string.h>
#include    <sys/stat.h>    /* for S_xxx file mode constants */
#include    <sys/uio.h>     /* for iovec{} and readv/writev */
#include    <unistd.h>
#include    <sys/wait.h>
#include    <sys/un.h>      /* for Unix domain sockets */
#include    <sys/select.h>  /* for convenience */
#include    <sys/param.h>   /* OpenBSD prereq for sysctl.h */
#include    <sys/sysctl.h>

#define MAXLINE 1024
#define LLINK  100
#define PORT 8888
#define SA  struct sockaddr

int main(int argc,char *argv[])
{
    if(argc!=2)
       {
        printf("error is argc\n");
        exit(0);
       }

    int sockfd;//本地套接字
    char buffsend[MAXLINE]="I'm a customer service provider";
    char buffrecv[MAXLINE];
    struct sockaddr_in servaddr;//服务器地址
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
        {
        printf("error is sockfd create\n");
        exit(0);
        }
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(PORT);//设置端口
    if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr)<0)
        {
        printf("IP set is error\n");
        exit(0);
        }
    if(connect(sockfd,(SA*)&servaddr,sizeof(servaddr))<0)//监听套接字
        {
        printf("error is connect\n ");
        exit(0);
        }
    printf(" buffsend :%s\n",buffsend);
    write(sockfd,buffsend,sizeof(buffsend));//给服务器发送消息

    bzero(buffrecv,sizeof(buffrecv));
    recv(sockfd,buffrecv,sizeof(buffrecv),0);
    printf("recv server message: %s\n",buffrecv);//打印客服端的消息
    close(sockfd);//关闭连接的套接字
  return 0;   
}

Makefile
all:
    gcc -g -o server server.c
    gcc -g -o client client.c
.PATH:
clean:
    rm -rf *.o server client
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值